summaryrefslogtreecommitdiffstats
path: root/js/src/tests/test262/built-ins/Temporal/ZonedDateTime
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/ZonedDateTime
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/ZonedDateTime')
-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
1245 files changed, 36738 insertions, 0 deletions
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);