summaryrefslogtreecommitdiffstats
path: root/js/src/tests/test262/built-ins/Temporal/PlainDateTime
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/PlainDateTime
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/PlainDateTime')
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/builtin.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-case-insensitive.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-number.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-temporal-object.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-undefined.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-wrong-type.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-number.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-object-insufficient-data.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-plaindate.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-case-insensitive.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-leap-second.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-number.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-string.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-wrong-type.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-year-zero.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-calendar-annotation.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-critical-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-date-with-utc-offset.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-multiple-calendar.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-multiple-time-zone.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-time-separators.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-time-zone-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-unknown-annotation.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-with-utc-designator.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-wrong-type.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-negative-epochnanoseconds.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/basic.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/builtin.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-datefromfields-called-with-null-prototype-fields.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-fields-iterable.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-ignored.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-temporal-object.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/cast.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/duplicate-calendar-fields.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/exhaustive.js167
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/infinity-throws-rangeerror.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/leap-second.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/not-a-constructor.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/read-time-fields-before-datefromfields.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/use-internal-slots.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/year-zero.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/constructor-full.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/constructor.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/datetime-math.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-number.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-object-month.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-object.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-plaindate.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-plaindatetime.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-case-insensitive.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-leap-second.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-number.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-calendar-annotation.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-comma-decimal-separator.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-date-with-utc-offset.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-invalid.js62
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-minus-sign.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-offset.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-optional-data.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-out-of-range.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-subsecond.js72
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-time-separators.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-time-zone-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-timezone.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-unknown-annotation.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-with-utc-designator.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-wrong-type.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-balance-negative-time-units.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-negative-epochnanoseconds.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/builtin.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/calendar-datefromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/calendar-fields-iterable.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/infinity-throws-rangeerror.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/leap-second.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/limits.js73
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/not-a-constructor.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/observable-get-overflow-argument-primitive.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/observable-get-overflow-argument-string-invalid.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/options-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/options-wrong-type.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/order-of-operations.js107
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-default-constrain.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-invalid-string.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-reject.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-undefined.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-wrong-type.js65
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/parser.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/read-time-fields-before-datefromfields.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/subclassing-ignored.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/hour-undefined.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/infinity-throws-rangeerror.js165
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/limits.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/microsecond-undefined.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/millisecond-undefined.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/minute-undefined.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/missing-arguments.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/nanosecond-undefined.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/negative-infinity-throws-rangeerror.js165
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/order-of-operations.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/ambiguous-date.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-duration-max.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-duration-out-of-range.js75
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-duration.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-invalid-property.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-mixed-sign.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-not-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-singular-properties.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-string-fractional-units-rounding-mode.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-string-negative-fractional-units.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/balance-negative-time-units.js52
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/calendar-dateadd.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/hour-overflow.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/limits.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/negative-duration.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/negative-infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/non-integer-throws-rangeerror.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-empty.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-invalid.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-undefined.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/order-of-operations.js127
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/overflow-invalid-string.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/overflow-undefined.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/overflow-wrong-type.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/constructor.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/basic.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/basic.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/basic.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/basic.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/basic.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-object-insufficient-data.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-plaindate.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-case-insensitive.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-leap-second.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-number.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-calendar-annotation.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-date-with-utc-offset.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-time-separators.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-time-zone-annotation.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-unknown-annotation.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-balance-negative-time-units.js44
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-negative-epochnanoseconds.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/basic.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-checked.js112
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-fields-iterable.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/cast.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/read-time-fields-before-datefromfields.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/custom.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-names.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-prop-desc.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-traversal-order.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/prototype.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/basic.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/validate-calendar-value.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/validate-calendar-value.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/basic.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/prop-desc.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/balance.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/limits.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/options-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/rounding-direction.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-divides.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-does-not-divide.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-nan.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-non-integer.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-one-day.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-out-of-range.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-undefined.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-wrong-type.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-basic.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-ceil.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-expand.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-floor.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfCeil.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfEven.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfExpand.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfFloor.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfTrunc.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfexpand-is-default.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-invalid-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-trunc.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-undefined.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-wrong-type.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundto-invalid-string.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-invalid-string.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-plurals-accepted.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-string-shorthand.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-wrong-type.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-argument-object-insufficient-data.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-argument-object.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-no-argument.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-object.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-plaindate.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-case-insensitive.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-leap-second.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-string.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-calendar-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-date-with-utc-offset.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-time-separators.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-time-zone-annotation.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-balance-negative-time-units.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-negative-epochnanoseconds.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/balance-negative-duration.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/balance-negative-time-units.js53
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateadd-called-with-plaindate-instance.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-copy-of-options.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-plaindate-calendar.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-fields-iterable.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/different-calendars-throws.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-invalid-string.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-plurals-accepted.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-smallestunit-mismatch.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/leap-second.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/no-unnecessary-units.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-empty.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-invalid.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/order-of-operations.js250
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/read-time-fields-before-datefromfields.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/returns-days.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/round-cross-unit-boundary.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/round-negative-duration.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/round-relative-to-receiver.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/rounding-zero-year-month-week-length.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-basic.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-cleanly-divides.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-does-not-divide.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-nan.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-non-integer.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-out-of-range.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-ceil.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-expand.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-floor.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfCeil.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfEven.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfExpand.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfFloor.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfTrunc.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfexpand-default-changes.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-invalid-string.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-trunc-is-default.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-trunc.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-undefined.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-invalid-string.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-plurals-accepted.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/subseconds.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/weeks-months-mutually-exclusive.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/ambiguous-date.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-duration-max.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-duration-out-of-range.js75
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-duration.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-invalid-property.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-mixed-sign.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-not-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-singular-properties.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string-fractional-units-rounding-mode.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string-negative-fractional-units.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/balance-negative-time-units.js52
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/calendar-dateadd.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/hour-overflow.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/limits.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/negative-duration.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/negative-infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/non-integer-throws-rangeerror.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-empty.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-invalid.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-undefined.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/order-of-operations.js127
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-invalid-string.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-undefined.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-wrong-type.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/basic.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/year-format.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/return-string.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/limits.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/builtin-calendar-no-observable-calls.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-arguments.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-fields-iterable.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-fromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-monthdayfromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/duplicate-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/basic.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/builtin-calendar-no-observable-calls.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-arguments.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-fields-iterable.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-fromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-yearmonthfromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/duplicate-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/basic.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendar-tostring.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-always.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-auto.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-critical.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-invalid-string.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-never.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-undefined.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-wrong-type.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-auto.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-invalid-string.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-nan.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-non-integer.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-number.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-out-of-range.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-undefined.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-wrong-type.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/options-object.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/options-undefined.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/order-of-operations.js70
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/rounding-cross-midnight.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/rounding-direction.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-ceil.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-expand.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-floor.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfCeil.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfEven.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfExpand.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfFloor.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfTrunc.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-invalid-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-trunc.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-wrong-type.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-fractionalseconddigits.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-invalid-string.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-plurals-accepted.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-undefined.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-valid-units.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-wrong-type.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/year-format.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toStringTag/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toStringTag/prop-desc.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toStringTag/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/balance-negative-time-units.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/basic.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguate-empty-possible-instants-with-datetime-near-limits.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-invalid-string.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-undefined.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/getpossibleinstantsfor-called-with-iso8601-calendar.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/invalid-instant.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/multiple-instants.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/options-object.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/options-undefined.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/order-of-operations.js65
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/plain-custom-timezone.js52
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/plain-date-time-near-limits.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-case-insensitive.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-not-callable.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-datetime.js65
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-leap-second.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-multiple-offsets.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-year-zero.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-wrong-type.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-object.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-plaindate.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-case-insensitive.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-leap-second.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-string.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-calendar-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-date-with-utc-offset.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-time-separators.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-time-zone-annotation.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-balance-negative-time-units.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-negative-epochnanoseconds.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/balance-negative-duration.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/balance-negative-time-units.js53
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/balance.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateadd-called-with-plaindate-instance.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-copy-of-options.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-plaindate-calendar.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-fields-iterable.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/casts-argument.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/different-calendars-throws.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/inverse.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-invalid-string.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-plurals-accepted.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-smallestunit-mismatch.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/leap-second.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/no-unnecessary-units.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-empty.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-invalid.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/order-of-operations.js250
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/read-time-fields-before-datefromfields.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/returns-days.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/round-cross-unit-boundary.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/round-negative-duration.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/round-relative-to-receiver.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/rounding-zero-year-month-week-length.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-basic.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-cleanly-divides.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-does-not-divide.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-nan.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-non-integer.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-out-of-range.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-ceil.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-expand.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-floor.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfCeil.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfEven.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfExpand.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfFloor.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfTrunc.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfexpand-default-changes.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-invalid-string.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-trunc-is-default.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-trunc.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-undefined.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-invalid-string.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-plurals-accepted.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/subseconds.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/units-changed.js69
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/weeks-months-mutually-exclusive.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/basic.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/basic.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/argument-not-object.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/argument-object-insufficient-data.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/basic.js80
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/builtin-calendar-no-observable-calls.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-fields-iterable.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-merge-fields-returns-primitive.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-options.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-temporal-object-throws.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-throws.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/copies-merge-fields-object.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/copy-properties-not-undefined.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/duplicate-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/infinity-throws-rangeerror.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/month-and-monthcode-must-agree.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/multiple-unrecognized-properties-ignored.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-empty.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-invalid.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-undefined.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/order-of-operations.js110
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/overflow-invalid-string.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/overflow-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/overflow-wrong-type.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/read-time-fields-before-datefromfields.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/string-throws.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/timezone-throws.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/argument-string.js57
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/basic.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-case-insensitive.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-number.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-string-leap-second.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-string.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-temporal-object.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-wrong-type.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/missing-argument.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/subclassing-ignored.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-leap-second.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-object-insufficient-data.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-object.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar-noniso.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar-same-id.js79
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar-same-object.js60
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindatetime.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-case-insensitive.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-leap-second.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-string.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-calendar-annotation.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-critical-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-date-with-utc-offset.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-invalid.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-iso-calendar.js54
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-time-separators.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-time-zone-annotation.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-unknown-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-datefromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-fields-iterable.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-temporal-object.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/non-compatible-calendars-throw.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-object-insufficient-data.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-calendar-annotation.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-critical-unknown-annotation.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-date-with-utc-offset.js53
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-multiple-time-zone.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-no-implicit-midnight.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-time-designator-required-for-disambiguation.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-time-separators.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-time-zone-annotation.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-unknown-annotation.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-with-time-designator.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-with-utc-designator.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-without-time-designator.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-time.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-wrong-type.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-balance-negative-time-units.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-negative-epochnanoseconds.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/leap-second.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/no-argument-default-to-midnight.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/plaintime-propertybag-no-time-units.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/time-undefined.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/year-zero.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/validate-calendar-value.js54
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/basic.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/validate-calendar-value.js54
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/second-undefined.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/subclass.js21
986 files changed, 28380 insertions, 0 deletions
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/builtin.js
new file mode 100644
index 0000000000..27b7011485
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/builtin.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Tests that Temporal.PlainDateTime meets the requirements for built-in objects
+info: |
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(typeof Temporal.PlainDateTime.prototype,
+ "object", "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-case-insensitive.js
new file mode 100644
index 0000000000..1f03ee3a0b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-case-insensitive.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Calendar names are case-insensitive
+features: [Temporal]
+---*/
+
+const arg = "iSo8601";
+
+const result = new Temporal.PlainDateTime(2000, 5, 2, 15, 23, 30, 987, 654, 321, arg);
+assert.sameValue(result.calendarId, "iso8601", "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-number.js
new file mode 100644
index 0000000000..a9482df5bc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-number.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: A number is not allowed to be a calendar
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ -19761118,
+ 19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => new Temporal.PlainDateTime(2000, 5, 2, 15, 23, 30, 987, 654, 321, arg),
+ "A number is not a valid ISO string for Calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-string.js
new file mode 100644
index 0000000000..0c2604006f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-string.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.constructor
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const arg = "iso8601";
+
+const result = new Temporal.PlainDateTime(2000, 5, 2, 15, 23, 30, 987, 654, 321, arg);
+assert.sameValue(result.getISOFields().calendar, "iso8601", `Calendar created from string "${arg}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-temporal-object.js
new file mode 100644
index 0000000000..60205456d5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-temporal-object.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal-totemporalcalendar step 1.b:
+ b. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2000, 5, 2);
+const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const plainMonthDay = new Temporal.PlainMonthDay(5, 2);
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 5);
+const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+[plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((arg) => {
+ const actual = [];
+ const expected = [];
+
+ const calendar = arg.getISOFields().calendar;
+
+ Object.defineProperty(arg, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ const result = new Temporal.PlainDateTime(2000, 5, 2, 15, 23, 30, 987, 654, 321, arg);
+ assert.sameValue(result.getISOFields().calendar, calendar, "Temporal object coerced to calendar");
+
+ assert.compareArray(actual, expected, "calendar getter not called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-undefined.js
new file mode 100644
index 0000000000..1ae70ea0ae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-undefined.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Calendar argument defaults to the built-in ISO 8601 calendar
+features: [Temporal]
+---*/
+
+const dateTimeArgs = [2020, 12, 24, 12, 34, 56, 123, 456, 789];
+
+Object.defineProperty(Temporal.Calendar, "from", {
+ get() {
+ throw new Test262Error("Should not get Calendar.from");
+ },
+});
+
+const dateTimeExplicit = new Temporal.PlainDateTime(...dateTimeArgs, undefined);
+assert.sameValue(dateTimeExplicit.calendarId, "iso8601");
+
+const dateTimeImplicit = new Temporal.PlainDateTime(...dateTimeArgs);
+assert.sameValue(dateTimeImplicit.calendarId, "iso8601");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-wrong-type.js
new file mode 100644
index 0000000000..593a7007a9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-wrong-type.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for Calendar
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => new Temporal.PlainDateTime(2000, 5, 2, 15, 23, 30, 987, 654, 321, arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => new Temporal.PlainDateTime(2000, 5, 2, 15, 23, 30, 987, 654, 321, arg), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..3f484cfd18
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const arg = { year: 2000, month: 5, day: 2, hour: 21, minute: 43, second: 5, calendar: "iso8601" };
+Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18));
+Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-number.js
new file mode 100644
index 0000000000..a14005f153
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-number.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: A number cannot be used in place of a Temporal.PlainDateTime
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)),
+ `A number (${arg}) is not a valid ISO string for PlainDateTime (first argument)`
+ );
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg),
+ `A number (${arg}) is not a valid ISO string for PlainDateTime (second argument)`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-object-insufficient-data.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-object-insufficient-data.js
new file mode 100644
index 0000000000..b8f944f95a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-object-insufficient-data.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: Plain object arguments may throw if they do not contain sufficient information
+features: [Temporal]
+---*/
+
+const dt1 = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+const dt2 = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102);
+
+assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.compare({ year: 1976 }, dt2),
+ "object must contain at least the required properties (first arg)"
+);
+
+assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.compare(dt1, { year: 2019 }),
+ "object must contain at least the required properties (second arg)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-plaindate.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-plaindate.js
new file mode 100644
index 0000000000..c5adfcf743
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-plaindate.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: Fast path for converting Temporal.PlainDate to Temporal.PlainDateTime by reading internal slots
+info: |
+ sec-temporal.plaindatetime.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalDateTime(_one_).
+ 2. Set _two_ to ? ToTemporalDateTime(_two_).
+ sec-temporal-totemporaldatetime step 2.b:
+ b. If _item_ has an [[InitializedTemporalDate]] internal slot, then
+ i. Return ? CreateTemporalDateTime(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], 0, 0, 0, 0, 0, 0, _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+TemporalHelpers.checkToTemporalPlainDateTimeFastPath((date) => {
+ const result = Temporal.PlainDateTime.compare(date, datetime);
+ assert.sameValue(result, -1, "PlainDate is converted to midnight");
+});
+
+TemporalHelpers.checkToTemporalPlainDateTimeFastPath((date) => {
+ const result = Temporal.PlainDateTime.compare(datetime, date);
+ assert.sameValue(result, 1, "PlainDate is converted to midnight");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..59b11ece54
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result1 = Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18));
+assert.sameValue(result1, 0, "Calendar is case-insensitive (first argument)");
+const result2 = Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg);
+assert.sameValue(result2, 0, "Calendar is case-insensitive (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..90391d682a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result1 = Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18));
+assert.sameValue(result1, 0, "leap second is a valid ISO string for calendar (first argument)");
+const result2 = Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg);
+assert.sameValue(result2, 0, "leap second is a valid ISO string for calendar (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..698f0cbb9f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-number.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)),
+ "A number is not a valid ISO string for calendar (first argument)"
+ );
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg),
+ "A number is not a valid ISO string for calendar (second argument)"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..759c69ab8e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-string.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateFromFields should not be looked up");
+ },
+});
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+
+const result1 = Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18));
+assert.sameValue(result1, 0, `Calendar created from string "${arg}" (first argument)`);
+
+const result2 = Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg);
+assert.sameValue(result2, 0, `Calendar created from string "${arg}" (second argument)`);
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", dateFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..7fc5b98699
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,49 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === "string" ? RangeError : TypeError,
+ () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)),
+ `${description} does not convert to a valid ISO string (first argument)`
+ );
+ assert.throws(
+ typeof calendar === "string" ? RangeError : TypeError,
+ () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg),
+ `${description} does not convert to a valid ISO string (second argument)`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)), `${description} is not a valid property bag and does not convert to a string (first argument)`);
+ assert.throws(TypeError, () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg), `${description} is not a valid property bag and does not convert to a string (second argument)`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..88fc524ec8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-year-zero.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)),
+ "reject minus zero as extended year (first argument)"
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg),
+ "reject minus zero as extended year (second argument)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..2543b83cd4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-calendar-annotation.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[u-ca=iso8601]", "without time zone"],
+ ["1976-11-18T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["1976-11-18T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["1976-11-18T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["1976-11-18T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainDateTime.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..4a51bd9821
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)),
+ `reject unknown annotation with critical flag: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg),
+ `reject unknown annotation with critical flag: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..49431f979f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-date-with-utc-offset.js
@@ -0,0 +1,51 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const validStrings = [
+ "1976-11-18T15:23+00:00",
+ "1976-11-18T15:23+00:00[UTC]",
+ "1976-11-18T15:23+00:00[!UTC]",
+ "1976-11-18T15:23-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = Temporal.PlainDateTime.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `"${arg}" is a valid UTC offset with time for PlainDateTime`
+ );
+}
+
+const invalidStrings = [
+ "2022-09-15Z",
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)),
+ `"${arg}" UTC offset without time is not valid for PlainDateTime (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg),
+ `"${arg}" UTC offset without time is not valid for PlainDateTime (second argument)`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..0505be4dfd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-multiple-calendar.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)),
+ `reject more than one calendar annotation if any critical: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg),
+ `reject more than one calendar annotation if any critical: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..93f1d3262a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-multiple-time-zone.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[UTC][UTC]",
+ "1970-01-01T00:00[!UTC][UTC]",
+ "1970-01-01T00:00[UTC][!UTC]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00[UTC][foo=bar][UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)),
+ `reject more than one time zone annotation: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg),
+ `reject more than one time zone annotation: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-time-separators.js
new file mode 100644
index 0000000000..3094da507c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-time-separators.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const dateTime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+const tests = [
+ ["1976-11-18T15:23", "uppercase T"],
+ ["1976-11-18t15:23", "lowercase T"],
+ ["1976-11-18 15:23", "space between date and time"],
+];
+
+tests.forEach(([arg, description]) => {
+ assert.sameValue(
+ Temporal.PlainDateTime.compare(arg, dateTime),
+ 0,
+ `variant time separators (${description}), first argument`
+ );
+
+ assert.sameValue(
+ Temporal.PlainDateTime.compare(dateTime, arg),
+ 0,
+ `variant time separators (${description}), second argument`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..b58759c3cf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-time-zone-annotation.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[Asia/Kolkata]", "named, with no offset"],
+ ["1976-11-18T15:23[!Europe/Vienna]", "named, with ! and no offset"],
+ ["1976-11-18T15:23[+00:00]", "numeric, with no offset"],
+ ["1976-11-18T15:23[!-02:30]", "numeric, with ! and no offset"],
+ ["1976-11-18T15:23+00:00[UTC]", "named, with offset"],
+ ["1976-11-18T15:23+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["1976-11-18T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["1976-11-18T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainDateTime.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..45ffdf03c3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-unknown-annotation.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[foo=bar]", "alone"],
+ ["1976-11-18T15:23[UTC][foo=bar]", "with time zone"],
+ ["1976-11-18T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["1976-11-18T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["1976-11-18T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainDateTime.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..35c5df333a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-with-utc-designator.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: RangeError thrown if a string with UTC designator is used as a PlainDateTime
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const dateTime = new Temporal.PlainDateTime(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.compare(arg, dateTime),
+ "String with UTC designator should not be valid as a PlainDateTime (first argument)"
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.compare(arg, dateTime),
+ "String with UTC designator should not be valid as a PlainDateTime (second argument)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-wrong-type.js
new file mode 100644
index 0000000000..efee691db5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-wrong-type.js
@@ -0,0 +1,47 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDateTime
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)),
+ `${description} does not convert to a valid ISO string (first argument)`
+ );
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg),
+ `${description} does not convert to a valid ISO string (second argument)`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDateTime, "Temporal.PlainDateTime, object"],
+ [Temporal.PlainDateTime.prototype, "Temporal.PlainDateTime.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)), `${description} is not a valid property bag and does not convert to a string (first argument)`);
+ assert.throws(TypeError, () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg), `${description} is not a valid property bag and does not convert to a string (second argument)`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..36f37fc751
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+---*/
+
+const zoned = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+const plain = new Temporal.PlainDateTime(1969, 7, 24, 16, 50, 35, 0, 0, 1);
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const result1 = Temporal.PlainDateTime.compare(plain, zoned);
+assert.sameValue(result1, 0);
+
+const result2 = Temporal.PlainDateTime.compare(zoned, plain);
+assert.sameValue(result2, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..c67b2534f1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, Infinity, -Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare(datetime, plain));
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare(plain, datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..0e209ef0e0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.compare(datetime, plain),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.compare(plain, datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..77f7867516
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare(datetime, plain));
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare(plain, datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..e88cdd726f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+ assert.throws(TypeError, () => Temporal.PlainDateTime.compare(datetime, plain));
+ assert.throws(TypeError, () => Temporal.PlainDateTime.compare(plain, datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/basic.js
new file mode 100644
index 0000000000..75ffb1d628
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/basic.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: Checking a typical case (nothing undefined, no NaNs, does not throw, etc.)
+features: [Temporal]
+---*/
+
+const dt1 = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+const dt2 = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102);
+const dt3 = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102);
+const dt4 = new Temporal.PlainDateTime(2019, 10, 29, 15, 23, 30, 123, 456, 789);
+const dt5 = new Temporal.PlainDateTime(1976, 11, 18, 10, 46, 38, 271, 986, 102);
+
+assert.sameValue(Temporal.PlainDateTime.compare(dt1, dt1), 0, "equal");
+assert.sameValue(Temporal.PlainDateTime.compare(dt1, dt2), -1, "smaller/larger");
+assert.sameValue(Temporal.PlainDateTime.compare(dt2, dt1), 1, "larger/smaller");
+assert.sameValue(Temporal.PlainDateTime.compare(dt2, dt3), 0, "equal different object");
+assert.sameValue(Temporal.PlainDateTime.compare(dt3, dt4), -1, "same date, earlier time");
+assert.sameValue(Temporal.PlainDateTime.compare(dt3, dt5), 1, "same time, later date");
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/builtin.js
new file mode 100644
index 0000000000..83800308ef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/builtin.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: Tests that Temporal.PlainDateTime.compare meets the requirements for built-in objects
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.compare),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.compare),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.compare),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.compare.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..7d5c0a293f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-datefromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const arg1 = { year: 2000, month: 5, day: 2, calendar };
+const arg2 = new Temporal.PlainDateTime(1976, 11, 18);
+
+Temporal.PlainDateTime.compare(arg1, arg2);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar (first argument)");
+
+calendar.dateFromFieldsCallCount = 0;
+
+Temporal.PlainDateTime.compare(arg2, arg1);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-fields-iterable.js
new file mode 100644
index 0000000000..33f0a9b7de
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-fields-iterable.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindatetime.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalDateTime(_one_).
+ 2. Set _two_ to ? ToTemporalDateTime(_two_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+Temporal.PlainDateTime.compare(
+ { year: 2000, month: 5, day: 2, calendar: calendar1 },
+ { year: 2001, month: 6, day: 3, calendar: calendar2 },
+);
+
+assert.sameValue(calendar1.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar1.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar1.iteratorExhausted[0], "iterated through the whole iterable");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-ignored.js
new file mode 100644
index 0000000000..1c533b58a3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-ignored.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: Calendar is not taken into account for the comparison.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar1 = TemporalHelpers.calendarThrowEverything();
+const calendar2 = TemporalHelpers.calendarThrowEverything();
+const dt1 = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar1);
+const dt2 = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102, calendar1);
+const dt3 = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102, calendar1);
+const dt4 = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102, calendar2);
+
+assert.sameValue(Temporal.PlainDateTime.compare(dt1, dt2), -1, "smaller");
+assert.sameValue(Temporal.PlainDateTime.compare(dt2, dt3), 0, "equal with same calendar");
+assert.sameValue(Temporal.PlainDateTime.compare(dt2, dt4), 0, "equal with different calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-temporal-object.js
new file mode 100644
index 0000000000..13b68d70e2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-temporal-object.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindatetime.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalDateTime(_one_).
+ 2. Set _two_ to ? ToTemporalDateTime(_two_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ Temporal.PlainDateTime.compare(
+ { year: 2000, month: 5, day: 2, calendar: temporalObject },
+ { year: 2001, month: 6, day: 3, calendar: temporalObject },
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/cast.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/cast.js
new file mode 100644
index 0000000000..dfd83e2876
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/cast.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: Arguments may be casted (string, plain object)
+features: [Temporal]
+---*/
+
+const dt1 = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+const dt2 = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102);
+
+assert.sameValue(
+ Temporal.PlainDateTime.compare({ year: 1976, month: 11, day: 18, hour: 15 }, dt2),
+ -1,
+ "casts first argument (plain object)"
+);
+
+assert.sameValue(
+ Temporal.PlainDateTime.compare("1976-11-18T15:23:30.123456789", dt2),
+ -1,
+ "casts first argument (string)"
+);
+
+assert.sameValue(
+ Temporal.PlainDateTime.compare(dt1, { year: 2019, month: 10, day: 29, hour: 10 }),
+ -1,
+ "casts second argument (plain object)"
+);
+
+assert.sameValue(
+ Temporal.PlainDateTime.compare(dt1, "2019-10-29T10:46:38.271986102"),
+ -1,
+ "casts second argument (string)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..a296792257
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/constructor-in-calendar-fields.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindatetime.prototype.compare
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+
+assert.throws(RangeError, () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)));
+assert.throws(RangeError, () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..7f573ce4d9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/duplicate-calendar-fields.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindatetime.prototype.compare
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['hour'], ['microsecond'], ['millisecond'], ['minute'], ['month'], ['monthCode'], ['nanosecond'], ['second'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)));
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/exhaustive.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/exhaustive.js
new file mode 100644
index 0000000000..0be8ab8d5c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/exhaustive.js
@@ -0,0 +1,167 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: Tests for compare() with each possible outcome
+features: [Temporal]
+---*/
+
+const cal1 = "iso8601";
+const cal2 = new (class extends Temporal.Calendar { id = "custom"; })("iso8601");
+
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 45, 333, 777, 111, cal1),
+ new Temporal.PlainDateTime(1987, 5, 31, 12, 15, 45, 333, 777, 111, cal2)
+ ),
+ 1,
+ "year >"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(1981, 12, 15, 6, 30, 15, 222, 444, 6, cal1),
+ new Temporal.PlainDateTime(2048, 12, 15, 6, 30, 15, 222, 444, 6, cal2)
+ ),
+ -1,
+ "year <"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 45, 333, 777, 111, cal1),
+ new Temporal.PlainDateTime(2000, 3, 31, 12, 15, 45, 333, 777, 111, cal2)
+ ),
+ 1,
+ "month >"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(1981, 4, 15, 6, 30, 15, 222, 444, 6, cal1),
+ new Temporal.PlainDateTime(1981, 12, 15, 6, 30, 15, 222, 444, 6, cal2)
+ ),
+ -1,
+ "month <"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 45, 333, 777, 111, cal1),
+ new Temporal.PlainDateTime(2000, 5, 14, 12, 15, 45, 333, 777, 111, cal2)
+ ),
+ 1,
+ "day >"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(1981, 4, 15, 6, 30, 15, 222, 444, 6, cal1),
+ new Temporal.PlainDateTime(1981, 4, 21, 6, 30, 15, 222, 444, 6, cal2)
+ ),
+ -1,
+ "day <"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 45, 333, 777, 111, cal1),
+ new Temporal.PlainDateTime(2000, 5, 31, 6, 15, 45, 333, 777, 111, cal2)
+ ),
+ 1,
+ "hour >"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(1981, 4, 15, 6, 30, 15, 222, 444, 6, cal1),
+ new Temporal.PlainDateTime(1981, 4, 15, 22, 30, 15, 222, 444, 6, cal2)
+ ),
+ -1,
+ "hour <"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 45, 333, 777, 111, cal1),
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 22, 333, 777, 111, cal2)
+ ),
+ 1,
+ "minute >"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(1981, 4, 15, 6, 30, 15, 222, 444, 6, cal1),
+ new Temporal.PlainDateTime(1981, 4, 15, 6, 57, 15, 222, 444, 6, cal2)
+ ),
+ -1,
+ "minute <"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 6, 333, 777, 111, cal1),
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 5, 333, 777, 111, cal2)
+ ),
+ 1,
+ "second >"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(1981, 4, 15, 6, 30, 3, 222, 444, 6, cal1),
+ new Temporal.PlainDateTime(1981, 4, 15, 6, 30, 4, 222, 444, 6, cal2)
+ ),
+ -1,
+ "second <"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 45, 6, 777, 111, cal1),
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 45, 5, 777, 111, cal2)
+ ),
+ 1,
+ "millisecond >"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(1981, 4, 15, 6, 30, 15, 3, 444, 6, cal1),
+ new Temporal.PlainDateTime(1981, 4, 15, 6, 30, 15, 4, 444, 6, cal2)
+ ),
+ -1,
+ "millisecond <"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 45, 333, 6, 111, cal1),
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 45, 333, 5, 111, cal2)
+ ),
+ 1,
+ "microsecond >"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(1981, 4, 15, 6, 30, 15, 222, 3, 6, cal1),
+ new Temporal.PlainDateTime(1981, 4, 15, 6, 30, 15, 222, 4, 6, cal2)
+ ),
+ -1,
+ "microsecond <"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 45, 333, 777, 999, cal1),
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 45, 333, 777, 111, cal2)
+ ),
+ 1,
+ "nanosecond >"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(1981, 4, 15, 6, 30, 15, 222, 444, 0, cal1),
+ new Temporal.PlainDateTime(1981, 4, 15, 6, 30, 15, 222, 444, 6, cal2)
+ ),
+ -1,
+ "nanosecond <"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 45, 333, 777, 111, cal1),
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 45, 333, 777, 111, cal2)
+ ),
+ 0,
+ "="
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..5e6ea86d52
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/infinity-throws-rangeerror.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in a property bag for either argument is Infinity or -Infinity
+esid: sec-temporal.plaindatetime.compare
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const other = new Temporal.PlainDateTime(2000, 5, 2, 15);
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare({ ...base, [prop]: inf }, other), `${prop} property cannot be ${inf}`);
+
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare(other, { ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls1 = [];
+ const obj1 = TemporalHelpers.toPrimitiveObserver(calls1, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare({ ...base, [prop]: obj1 }, other));
+ assert.compareArray(calls1, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+
+ const calls2 = [];
+ const obj2 = TemporalHelpers.toPrimitiveObserver(calls2, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare(other, { ...base, [prop]: obj2 }));
+ assert.compareArray(calls2, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/leap-second.js
new file mode 100644
index 0000000000..fe8e047f09
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/leap-second.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: Leap second is a valid ISO string for PlainDateTime
+features: [Temporal]
+---*/
+
+let arg = "2016-12-31T23:59:60";
+const result1 = Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(2016, 12, 31, 23, 59, 59));
+assert.sameValue(result1, 0, "leap second is a valid ISO string for PlainDateTime (first argument)");
+const result2 = Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(2016, 12, 31, 23, 59, 59), arg);
+assert.sameValue(result2, 0, "leap second is a valid ISO string for PlainDateTime (second argument)");
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+
+const result3 = Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(2016, 12, 31, 23, 59, 59));
+assert.sameValue(result3, 0, "second: 60 is constrained in property bag for PlainDateTime (first argument)");
+const result4 = Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(2016, 12, 31, 23, 59, 59), arg);
+assert.sameValue(result4, 0, "second: 60 is constrained in property bag for PlainDateTime (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/length.js
new file mode 100644
index 0000000000..6c31f059c0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: Temporal.PlainDateTime.compare.length is 2
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.compare, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/name.js
new file mode 100644
index 0000000000..0e55f30590
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: Temporal.PlainDateTime.compare.name is "compare"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.compare, "name", {
+ value: "compare",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/not-a-constructor.js
new file mode 100644
index 0000000000..995cc79eff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/not-a-constructor.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: Temporal.PlainDateTime.compare does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.compare();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.compare), false,
+ "isConstructor(Temporal.PlainDateTime.compare)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/prop-desc.js
new file mode 100644
index 0000000000..ecdfcdc3a3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: The "compare" property of Temporal.PlainDateTime
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.compare,
+ "function",
+ "`typeof PlainDateTime.compare` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime, "compare", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..694e3f2928
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/proto-in-calendar-fields.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindatetime.prototype.compare
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+
+assert.throws(RangeError, () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)));
+assert.throws(RangeError, () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/read-time-fields-before-datefromfields.js
new file mode 100644
index 0000000000..37527a010e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/read-time-fields-before-datefromfields.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.plaindatetime.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalDateTime(_one_).
+ 2. Set _two_ to ? ToTemporalDateTime(_two_).
+ sec-temporal-totemporaldatetime step 2.e:
+ e. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const result = Temporal.PlainDateTime.compare(
+ { year: 2000, month: 5, day: 2, hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321, calendar },
+ { year: 2000, month: 5, day: 2, hour: 6, minute: 54, second: 32, millisecond: 123, microsecond: 456, nanosecond: 789, calendar },
+);
+
+// will be 0 if the time fields are coerced to their max values due to Infinity
+assert.sameValue(result, 1, "comparison result");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/use-internal-slots.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/use-internal-slots.js
new file mode 100644
index 0000000000..8899f71885
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/use-internal-slots.js
@@ -0,0 +1,47 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-compareisodatetime
+description: compare() ignores the observable properties and uses internal slots
+features: [Temporal]
+---*/
+
+function CustomError() {}
+
+class AvoidGettersDateTime extends Temporal.PlainDateTime {
+ get year() {
+ throw new CustomError();
+ }
+ get month() {
+ throw new CustomError();
+ }
+ get day() {
+ throw new CustomError();
+ }
+ get hour() {
+ throw new CustomError();
+ }
+ get minute() {
+ throw new CustomError();
+ }
+ get second() {
+ throw new CustomError();
+ }
+ get millisecond() {
+ throw new CustomError();
+ }
+ get microsecond() {
+ throw new CustomError();
+ }
+ get nanosecond() {
+ throw new CustomError();
+ }
+}
+
+const one = new AvoidGettersDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const two = new AvoidGettersDateTime(2006, 3, 25, 6, 54, 32, 123, 456, 789);
+assert.sameValue(Temporal.PlainDateTime.compare(one, two), -1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/year-zero.js
new file mode 100644
index 0000000000..90ed2d8408
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/year-zero.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Negative zero, as an extended year, fails
+esid: sec-temporal.plaindatetime.compare
+features: [Temporal]
+---*/
+
+const ok = new Temporal.PlainDateTime(2000, 5, 2, 15);
+const invalidStrings = [
+ "-000000-12-07",
+ "-000000-12-07T03:24:30",
+ "-000000-12-07T03:24:30+01:00",
+ "-000000-12-07T03:24:30+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(RangeError,
+ () => Temporal.PlainDateTime.compare(arg, ok),
+ "Cannot use minus zero as extended year (first argument)"
+ );
+
+ assert.throws(RangeError,
+ () => Temporal.PlainDateTime.compare(ok, arg),
+ "Cannot use minus zero as extended year (second argument)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/constructor-full.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/constructor-full.js
new file mode 100644
index 0000000000..a0ff2ad2da
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/constructor-full.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Checking an explicitly constructed instance with all arguments
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const calendar = Temporal.Calendar.from("iso8601");
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar);
+
+TemporalHelpers.assertPlainDateTime(datetime,
+ 1976, 11, "M11", 18, 15, 23, 30, 123, 456, 789,
+ "check instance (all arguments supplied)"
+);
+
+assert.sameValue(
+ datetime.getCalendar(),
+ calendar,
+ "calendar supplied in constructor can be extracted and is unchanged"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/constructor.js
new file mode 100644
index 0000000000..9041542e74
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/constructor.js
@@ -0,0 +1,15 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Temporal.PlainDateTime constructor cannot be called as a function
+info: |
+ 1. If NewTarget is undefined, throw a TypeError exception.
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => Temporal.PlainDateTime(1970, 1, 2, 12, 0, 0));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/datetime-math.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/datetime-math.js
new file mode 100644
index 0000000000..5b57f4bcbd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/datetime-math.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Testing combinations of since, until, add, subtract, and negated
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const earlier = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102);
+const units = ["years", "months", "weeks", "days", "hours", "minutes", "seconds"];
+
+units.forEach((largestUnit) => {
+ const diff = later.since(earlier, { largestUnit });
+ TemporalHelpers.assertDurationsEqual(
+ earlier.since(later, { largestUnit }),
+ diff.negated(),
+ `(${earlier}).since(${later}) == (${later}).since(${earlier}).negated()`
+ );
+ TemporalHelpers.assertDurationsEqual(
+ earlier.until(later, { largestUnit }),
+ diff,
+ `(${earlier}).until(${later}) == (${later}).since(${earlier})`
+ );
+ assert.sameValue(
+ earlier.add(diff).equals(later),
+ true,
+ `(${earlier}).add(${diff}) == (${later})`
+ );
+ assert.sameValue(
+ later.subtract(diff).equals(earlier),
+ true,
+ `(${later}).subtract(${diff}) == (${earlier})`
+ );
+ assert.sameValue(
+ earlier.subtract(diff.negated()).equals(later),
+ true,
+ "symmetrical with regard to negative durations (1)"
+ );
+ assert.sameValue(
+ later.add(diff.negated()).equals(earlier),
+ true,
+ "symmetrical with regard to negative durations (2)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..c21adc332a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const arg = { year: 2000, month: 5, day: 2, hour: 21, minute: 43, second: 5, calendar: "iso8601" };
+Temporal.PlainDateTime.from(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-number.js
new file mode 100644
index 0000000000..52aa463b20
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-number.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: A number cannot be used in place of a Temporal.PlainDateTime
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.from(arg),
+ `A number (${arg}) is not a valid ISO string for PlainDateTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-object-month.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-object-month.js
new file mode 100644
index 0000000000..6370d394ed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-object-month.js
@@ -0,0 +1,35 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: A plain object argument needs to specify a month
+features: [Temporal]
+---*/
+
+assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from({year: 1976, month: 11, monthCode: "M12", day: 18}),
+ "month and monthCode must agree"
+);
+
+assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.from({year: 1976, month: undefined, monthCode: undefined, day: 18}),
+ "required prop undefined throws"
+);
+
+assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.from({year: 1976, day: 18, hour: 15, minute: 23, second: 30, millisecond: 123}),
+ "required prop missing throws"
+);
+
+assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.from({year: 1976, months: 11, day: 18}),
+ "plural \"months\" is not acceptable"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-object.js
new file mode 100644
index 0000000000..8c98885a34
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-object.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Plain objects are acceptable
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from({year: 1976, month: 11, monthCode: "M11", day: 18}),
+ 1976, 11, "M11", 18, 0, 0, 0, 0, 0, 0,
+ "plain object with month & month code"
+);
+
+assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.from({}),
+ "empty object throws"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from({year: 1976, month: 11, day: 18, millisecond: 123}),
+ 1976, 11, "M11", 18, 0, 0, 0, 123, 0, 0,
+ "plain object with month but not month code"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from({year: 1976, monthCode: "M09", day: 18, millisecond: 123}),
+ 1976, 9, "M09", 18, 0, 0, 0, 123, 0, 0,
+ "plain object with month code but not month"
+);
+
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from({year: 1976, month: 11, day: 18, hours: 12}),
+ 1976, 11, "M11", 18, 0, 0, 0, 0, 0, 0,
+ "incorrectly-spelled properties (e.g., plural \"hours\") are ignored"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-plaindate.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-plaindate.js
new file mode 100644
index 0000000000..1810a29a46
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-plaindate.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Fast path for converting Temporal.PlainDate to Temporal.PlainDateTime by reading internal slots
+info: |
+ sec-temporal.plaindatetime.from step 3:
+ 3. Return ? ToTemporalDateTime(_item_, _options_).
+ sec-temporal-totemporaldatetime step 2.b:
+ b. If _item_ has an [[InitializedTemporalDate]] internal slot, then
+ i. Return ? CreateTemporalDateTime(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], 0, 0, 0, 0, 0, 0, _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalPlainDateTimeFastPath((date, calendar) => {
+ const result = Temporal.PlainDateTime.from(date);
+ TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 0, 0, 0, 0, 0, 0, "midnight is assumed");
+ assert.sameValue(result.getCalendar(), calendar, "calendar result");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-plaindatetime.js
new file mode 100644
index 0000000000..62662de9f3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-plaindatetime.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: A PlainDateTime object is copied, not returned directly
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const orig = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 20, 123, 456, 789);
+const result = Temporal.PlainDateTime.from(orig);
+
+TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 15, 23, 20, 123, 456, 789,
+ "PlainDateTime is copied"
+);
+
+assert.sameValue(result.getISOFields().calendar, orig.getISOFields().calendar, "Calendar is copied");
+
+assert.notSameValue(
+ result,
+ orig,
+ "When a PlainDateTime is given, the returned value is not the original PlainDateTime"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..d09e414fc6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: The calendar name is case-insensitive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = Temporal.PlainDateTime.from(arg);
+TemporalHelpers.assertPlainDateTime(result, 1976, 11, "M11", 18, 0, 0, 0, 0, 0, 0, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..bb4d829571
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Leap second is a valid ISO string for a calendar in a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = Temporal.PlainDateTime.from(arg);
+TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..7ed5518d75
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-number.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.from(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..2c86bf26b2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-string.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = Temporal.PlainDateTime.from(arg);
+TemporalHelpers.assertPlainDateTime(result, 1976, 11, "M11", 18, 0, 0, 0, 0, 0, 0, `Calendar created from string "${calendar}"`);
+assert.sameValue(result.getISOFields().calendar, "iso8601", "calendar slot stores a string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..24dac75fc2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => Temporal.PlainDateTime.from(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => Temporal.PlainDateTime.from(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..8e5bc48a68
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-year-zero.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..a0d058a977
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-calendar-annotation.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[u-ca=iso8601]", "without time zone"],
+ ["1976-11-18T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["1976-11-18T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["1976-11-18T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["1976-11-18T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainDateTime.from(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 15, 23, 0, 0, 0, 0,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-comma-decimal-separator.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-comma-decimal-separator.js
new file mode 100644
index 0000000000..66d90e42a3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-comma-decimal-separator.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Comma may be used as a decimal separator
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30,12"),
+ 1976, 11, "M11", 18, 15, 23, 30, 120, 0, 0,
+ "comma decimal separator"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..d8767b4a80
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..37ea330410
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-date-with-utc-offset.js
@@ -0,0 +1,47 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const validStrings = [
+ "1976-11-18T15:23+00:00",
+ "1976-11-18T15:23+00:00[UTC]",
+ "1976-11-18T15:23+00:00[!UTC]",
+ "1976-11-18T15:23-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = Temporal.PlainDateTime.from(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 15, 23, 0, 0, 0, 0,
+ `"${arg}" is a valid UTC offset with time for PlainDateTime`
+ );
+}
+
+const invalidStrings = [
+ "2022-09-15Z",
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDateTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-invalid.js
new file mode 100644
index 0000000000..0d086de776
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-invalid.js
@@ -0,0 +1,62 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Reject string argument if it cannot be parsed
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "obviously invalid",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDateTime:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+
+invalidStrings.forEach((s) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from(s),
+ `invalid date-time string (${s})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-minus-sign.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-minus-sign.js
new file mode 100644
index 0000000000..f2664a50cc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-minus-sign.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Non-ASCII minus sign is acceptable
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30.12\u221202:00"),
+ 1976, 11, "M11", 18, 15, 23, 30, 120, 0, 0,
+ "variant minus sign (offset)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("\u2212009999-11-18T15:23:30.12"),
+ -9999, 11, "M11", 18, 15, 23, 30, 120, 0, 0,
+ "variant minus sign (leading minus)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..7ac0aaa699
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-multiple-calendar.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..a50b38b586
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-multiple-time-zone.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[UTC][UTC]",
+ "1970-01-01T00:00[!UTC][UTC]",
+ "1970-01-01T00:00[UTC][!UTC]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00[UTC][foo=bar][UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-offset.js
new file mode 100644
index 0000000000..a302a19e9b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-offset.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Extended format may be used
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const expected = [1976, 11, "M11", 18, 15, 23, 30, 100, 0, 0];
+
+const strs = [
+ "1976-11-18T152330.1+00:00",
+ "19761118T15:23:30.1+00:00",
+ "1976-11-18T15:23:30.1+0000",
+ "1976-11-18T152330.1+0000",
+ "19761118T15:23:30.1+0000",
+ "19761118T152330.1+00:00",
+ "19761118T152330.1+0000",
+ "+001976-11-18T152330.1+00:00",
+ "+0019761118T15:23:30.1+00:00",
+ "+001976-11-18T15:23:30.1+0000",
+ "+001976-11-18T152330.1+0000",
+ "+0019761118T15:23:30.1+0000",
+ "+0019761118T152330.1+00:00",
+ "+0019761118T152330.1+0000"
+];
+
+strs.forEach((s) => {
+ TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from(s),
+ ...expected,
+ `mixture of basic and extended format (${s})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-optional-data.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-optional-data.js
new file mode 100644
index 0000000000..3eb56946e7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-optional-data.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Some parts of a string argument may be omitted
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30+00"),
+ 1976, 11, "M11", 18, 15, 23, 30, 0, 0, 0,
+ "optional parts (no minute after offset)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15"),
+ 1976, 11, "M11", 18, 15, 0, 0, 0, 0, 0,
+ "optional parts (no minute in time part)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18"),
+ 1976, 11, "M11", 18, 0, 0, 0, 0, 0, 0,
+ "optional parts (no time part)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-out-of-range.js
new file mode 100644
index 0000000000..756387cd55
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-out-of-range.js
@@ -0,0 +1,35 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Invalid ISO string not acceptable even with overflow = constrain
+features: [Temporal]
+---*/
+
+assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from("2020-13-34T24:60", {}),
+ "constrain has no effect on invalid ISO string (empty options argument)"
+);
+
+assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from("2020-13-34T24:60", () => {}),
+ "constrain has no effect on invalid ISO string (nullary empty object function argument)"
+);
+
+assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from("2020-13-34T24:60", {overflow: "constrain"}),
+ "overflow = constrain has no effect on invalid ISO string"
+);
+
+assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from("2020-13-34T24:60", {overflow: "reject"}),
+ "overflow = reject has no effect on invalid ISO string"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-subsecond.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-subsecond.js
new file mode 100644
index 0000000000..8f44695463
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-subsecond.js
@@ -0,0 +1,72 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Up to nine digits of sub-second precision are acceptable
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30.1"),
+ 1976, 11, "M11", 18, 15, 23, 30, 100, 0, 0,
+ "various precisions are possible (one decimal digit)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30.12"),
+ 1976, 11, "M11", 18, 15, 23, 30, 120, 0, 0,
+ "various precisions are possible (two decimal digits)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30.123"),
+ 1976, 11, "M11", 18, 15, 23, 30, 123, 0, 0,
+ "various precisions are possible (three decimal digits)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30.1234"),
+ 1976, 11, "M11", 18, 15, 23, 30, 123, 400, 0,
+ "various precisions are possible (four decimal digits)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30.12345"),
+ 1976, 11, "M11", 18, 15, 23, 30, 123, 450, 0,
+ "various precisions are possible (five decimal digits)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30.123456"),
+ 1976, 11, "M11", 18, 15, 23, 30, 123, 456, 0,
+ "various precisions are possible (six decimal digits)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30.1234567"),
+ 1976, 11, "M11", 18, 15, 23, 30, 123, 456, 700,
+ "various precisions are possible (seven decimal digits)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30.12345678"),
+ 1976, 11, "M11", 18, 15, 23, 30, 123, 456, 780,
+ "various precisions are possible (eight decimal digits)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30.123456789"),
+ 1976, 11, "M11", 18, 15, 23, 30, 123, 456, 789,
+ "various precisions are possible (nine decimal digits)"
+);
+
+assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from("1976-11-18T15:23:30.1234567891"),
+ "ten decimal digits is too much"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-time-separators.js
new file mode 100644
index 0000000000..c9118d68a4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-time-separators.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23", "uppercase T"],
+ ["1976-11-18t15:23", "lowercase T"],
+ ["1976-11-18 15:23", "space between date and time"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainDateTime.from(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 15, 23, 0, 0, 0, 0,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..fa321306a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-time-zone-annotation.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[Asia/Kolkata]", "named, with no offset"],
+ ["1976-11-18T15:23[!Europe/Vienna]", "named, with ! and no offset"],
+ ["1976-11-18T15:23[+00:00]", "numeric, with no offset"],
+ ["1976-11-18T15:23[!-02:30]", "numeric, with ! and no offset"],
+ ["1976-11-18T15:23+00:00[UTC]", "named, with offset"],
+ ["1976-11-18T15:23+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["1976-11-18T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["1976-11-18T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainDateTime.from(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 15, 23, 0, 0, 0, 0,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-timezone.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-timezone.js
new file mode 100644
index 0000000000..5c5810dfa7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-timezone.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Timezone, if specified, is ignored
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("2020-01-01T01:23:45[Asia/Kolkata]"),
+ 2020, 1, "M01", 1, 1, 23, 45, 0, 0, 0,
+ "ignores if a timezone is specified"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..0c115b79bf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-unknown-annotation.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[foo=bar]", "alone"],
+ ["1976-11-18T15:23[UTC][foo=bar]", "with time zone"],
+ ["1976-11-18T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["1976-11-18T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["1976-11-18T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainDateTime.from(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 15, 23, 0, 0, 0, 0,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..825d2da94b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-with-utc-designator.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: RangeError thrown if a string with UTC designator is used as a PlainDateTime
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from(arg),
+ "String with UTC designator should not be valid as a PlainDateTime"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string.js
new file mode 100644
index 0000000000..2addaccc70
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: String arguments are acceptable
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30"),
+ 1976, 11, "M11", 18, 15, 23, 30, 0, 0, 0,
+ "date and time (no subseconds)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30.001"),
+ 1976, 11, "M11", 18, 15, 23, 30, 1, 0, 0,
+ "date and time (millisecond)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30.001123"),
+ 1976, 11, "M11", 18, 15, 23, 30, 1, 123, 0,
+ "date and time (microsecond)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30.001123456"),
+ 1976, 11, "M11", 18, 15, 23, 30, 1, 123, 456,
+ "date and time (nanosecond)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-wrong-type.js
new file mode 100644
index 0000000000..13deec81ce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-wrong-type.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDateTime
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => Temporal.PlainDateTime.from(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDateTime, "Temporal.PlainDateTime, object"],
+ [Temporal.PlainDateTime.prototype, "Temporal.PlainDateTime.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.PlainDateTime.from(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 0000000000..0f839b62d9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,46 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Negative time fields are balanced upwards if the argument is given as ZonedDateTime
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-temporal-totemporaldatetime step 3.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ ii. 1. Return ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
+ sec-temporal.plaindatetime.from step 3:
+ 3. Return ? ToTemporalDateTime(_temporalTime_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// This code path is encountered if the time zone offset is negative and its
+// absolute value in nanoseconds is greater than the nanosecond field of the
+// exact time's epoch parts
+const tz = TemporalHelpers.specificOffsetTimeZone(-2);
+const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
+
+const pdt = Temporal.PlainDateTime.from(datetime);
+
+TemporalHelpers.assertPlainDateTime(pdt, 1970, 1, "M01", 1, 1, 1, 1, 1, 0, 999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..774cf3d994
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const result = Temporal.PlainDateTime.from(datetime);
+TemporalHelpers.assertPlainDateTime(result, 1969, 7, "M07", 24, 16, 50, 35, 0, 0, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..828f40a13b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => Temporal.PlainDateTime.from(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..f9e2bc0f90
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindatetime.from
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach(notCallable => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.from(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..cc4c3210eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => Temporal.PlainDateTime.from(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..8f3a854e1f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => Temporal.PlainDateTime.from(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/builtin.js
new file mode 100644
index 0000000000..08e167da13
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/builtin.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Tests that Temporal.PlainDateTime.from meets the requirements for built-in objects
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.from),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.from),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.from),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.from.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..0345c38c27
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/calendar-datefromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const arg = { year: 2000, month: 5, day: 2, calendar };
+Temporal.PlainDateTime.from(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/calendar-fields-iterable.js
new file mode 100644
index 0000000000..bdf5cdc7ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/calendar-fields-iterable.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindatetime.from step 3:
+ 3. Return ? ToTemporalDateTime(_item_, _options_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, calendar });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/calendar-temporal-object.js
new file mode 100644
index 0000000000..f986d85777
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindatetime.from step 3:
+ 3. Return ? ToTemporalDateTime(_item_, _options_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => {
+ const result = Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+ assert.sameValue(result.getCalendar(), calendar, "Temporal object coerced to calendar");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..4df5ca8f0d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindatetime.prototype.from
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+
+assert.throws(RangeError, () => Temporal.PlainDateTime.from(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..8ad203ebea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindatetime.prototype.from
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['hour'], ['microsecond'], ['millisecond'], ['minute'], ['month'], ['monthCode'], ['nanosecond'], ['second'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+
+ assert.throws(RangeError, () => Temporal.PlainDateTime.from(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..d35c081317
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/infinity-throws-rangeerror.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindatetime.from
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => Temporal.PlainDateTime.from({ ...base, [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainDateTime.from({ ...base, [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/leap-second.js
new file mode 100644
index 0000000000..efbcdd8cd6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/leap-second.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Leap second is a valid ISO string for PlainDateTime
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let arg = "2016-12-31T23:59:60";
+
+const result1 = Temporal.PlainDateTime.from(arg);
+TemporalHelpers.assertPlainDateTime(
+ result1,
+ 2016, 12, "M12", 31, 23, 59, 59, 0, 0, 0,
+ "leap second is a valid ISO string for PlainDateTime"
+);
+
+const result2 = Temporal.PlainDateTime.from(arg);
+TemporalHelpers.assertPlainDateTime(
+ result2,
+ 2016, 12, "M12", 31, 23, 59, 59, 0, 0, 0,
+ "leap second is a valid ISO string for PlainDateTime even with overflow: reject"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+
+const result3 = Temporal.PlainDateTime.from(arg);
+TemporalHelpers.assertPlainDateTime(
+ result3,
+ 2016, 12, "M12", 31, 23, 59, 59, 0, 0, 0,
+ "second: 60 is constrained in property bag for PlainDateTime"
+);
+
+assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from(arg, { overflow: "reject" }),
+ "second: 60 is rejected in property bag for PlainDateTime with overflow: reject"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/length.js
new file mode 100644
index 0000000000..f547f0c619
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Temporal.PlainDateTime.from.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.from, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/limits.js
new file mode 100644
index 0000000000..af997c2688
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/limits.js
@@ -0,0 +1,73 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Checking limits of representable PlainDateTime
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+["reject", "constrain"].forEach((overflow) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from({ year: -271821, month: 4, day: 19 }, { overflow }),
+ `negative out of bounds (plain object, overflow = ${overflow})`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from({ year: 275760, month: 9, day: 14 }, { overflow }),
+ `positive out of bounds (plain object, overflow = ${overflow})`
+ );
+});
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from({ year: -271821, month: 4, day: 19, nanosecond: 1 }),
+ -271821, 4, "M04", 19, 0, 0, 0, 0, 0, 1,
+ "construct from property bag (negative boundary)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from(
+ {
+ year: 275760,
+ month: 9,
+ day: 13,
+ hour: 23,
+ minute: 59,
+ second: 59,
+ millisecond: 999,
+ microsecond: 999,
+ nanosecond: 999
+ }
+ ),
+ 275760, 9, "M09", 13, 23, 59, 59, 999, 999, 999,
+ "construct from property bag (positive boundary)"
+);
+
+assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from("-271821-04-19T00:00"),
+ "out-of-bounds ISO string (negative case)"
+);
+
+assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from("+275760-09-14T00:00"),
+ "out-of-bounds ISO string (positive case)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("-271821-04-19T00:00:00.000000001"),
+ -271821, 4, "M04", 19, 0, 0, 0, 0, 0, 1,
+ "boundary ISO string (negative case)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("+275760-09-13T23:59:59.999999999"),
+ 275760, 9, "M09", 13, 23, 59, 59, 999, 999, 999,
+ "boundary ISO string (positive case)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/name.js
new file mode 100644
index 0000000000..633e22a791
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Temporal.PlainDateTime.from.name is "from"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.from, "name", {
+ value: "from",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/not-a-constructor.js
new file mode 100644
index 0000000000..9ca650011d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/not-a-constructor.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Temporal.PlainDateTime.from does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.from();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.from), false,
+ "isConstructor(Temporal.PlainDateTime.from)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/observable-get-overflow-argument-primitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/observable-get-overflow-argument-primitive.js
new file mode 100644
index 0000000000..5dcf84541d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/observable-get-overflow-argument-primitive.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: overflow property is extracted with string argument.
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+
+let actual = [];
+const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "reject" }, "options");
+
+const result = Temporal.PlainDateTime.from("2021-05-17T12:34:56", options);
+assert.compareArray(actual, expected, "Successful call");
+TemporalHelpers.assertPlainDateTime(result, 2021, 5, "M05", 17, 12, 34, 56, 0, 0, 0);
+
+actual.splice(0); // empty it for the next check
+const failureExpected = [
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+];
+
+assert.throws(TypeError, () => Temporal.PlainDateTime.from(7, options));
+assert.compareArray(actual, failureExpected, "Failing call");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/observable-get-overflow-argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/observable-get-overflow-argument-string-invalid.js
new file mode 100644
index 0000000000..799c8e70ea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/observable-get-overflow-argument-string-invalid.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: overflow property is extracted with ISO-invalid string argument.
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+];
+
+let actual = [];
+const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "reject" }, "options");
+
+assert.throws(RangeError, () => Temporal.PlainDateTime.from("2020-13-34T25:60:60", options));
+assert.compareArray(actual, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/options-object.js
new file mode 100644
index 0000000000..7824114ba5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/options-object.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.from
+description: Empty object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from({ year: 1976, month: 11, day: 18 }, {}), 1976, 11, "M11", 18, 0, 0, 0, 0, 0, 0,
+ "options may be an empty plain object"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from({ year: 1976, month: 11, day: 18 }, () => {}), 1976, 11, "M11", 18, 0, 0, 0, 0, 0, 0,
+ "options may be an empty function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/options-undefined.js
new file mode 100644
index 0000000000..61903bbced
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/options-undefined.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const fields = { year: 2000, month: 13, day: 2 };
+
+const explicit = Temporal.PlainDateTime.from(fields, undefined);
+assert.sameValue(explicit.month, 12, "default overflow is constrain");
+
+const implicit = Temporal.PlainDateTime.from(fields);
+assert.sameValue(implicit.month, 12, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/options-wrong-type.js
new file mode 100644
index 0000000000..93b852dda0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/options-wrong-type.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+for (const value of badOptions) {
+ assert.throws(TypeError, () => Temporal.PlainDateTime.from({ year: 1976, month: 11, day: 18 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/order-of-operations.js
new file mode 100644
index 0000000000..8c40100fdb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/order-of-operations.js
@@ -0,0 +1,107 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Properties on an object passed to from() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ "getOwnPropertyDescriptor options.extra",
+ "get options.extra",
+ // GetTemporalCalendarSlotValueWithISODefault
+ "get fields.calendar",
+ "has fields.calendar.dateAdd",
+ "has fields.calendar.dateFromFields",
+ "has fields.calendar.dateUntil",
+ "has fields.calendar.day",
+ "has fields.calendar.dayOfWeek",
+ "has fields.calendar.dayOfYear",
+ "has fields.calendar.daysInMonth",
+ "has fields.calendar.daysInWeek",
+ "has fields.calendar.daysInYear",
+ "has fields.calendar.fields",
+ "has fields.calendar.id",
+ "has fields.calendar.inLeapYear",
+ "has fields.calendar.mergeFields",
+ "has fields.calendar.month",
+ "has fields.calendar.monthCode",
+ "has fields.calendar.monthDayFromFields",
+ "has fields.calendar.monthsInYear",
+ "has fields.calendar.weekOfYear",
+ "has fields.calendar.year",
+ "has fields.calendar.yearMonthFromFields",
+ "has fields.calendar.yearOfWeek",
+ // lookup
+ "get fields.calendar.dateFromFields",
+ "get fields.calendar.fields",
+ // CalendarFields
+ "call fields.calendar.fields",
+ // PrepareTemporalFields
+ "get fields.day",
+ "get fields.day.valueOf",
+ "call fields.day.valueOf",
+ "get fields.hour",
+ "get fields.hour.valueOf",
+ "call fields.hour.valueOf",
+ "get fields.microsecond",
+ "get fields.microsecond.valueOf",
+ "call fields.microsecond.valueOf",
+ "get fields.millisecond",
+ "get fields.millisecond.valueOf",
+ "call fields.millisecond.valueOf",
+ "get fields.minute",
+ "get fields.minute.valueOf",
+ "call fields.minute.valueOf",
+ "get fields.month",
+ "get fields.month.valueOf",
+ "call fields.month.valueOf",
+ "get fields.monthCode",
+ "get fields.monthCode.toString",
+ "call fields.monthCode.toString",
+ "get fields.nanosecond",
+ "get fields.nanosecond.valueOf",
+ "call fields.nanosecond.valueOf",
+ "get fields.second",
+ "get fields.second.valueOf",
+ "call fields.second.valueOf",
+ "get fields.year",
+ "get fields.year.valueOf",
+ "call fields.year.valueOf",
+ // InterpretTemporalDateTimeFields
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+ "call fields.calendar.dateFromFields",
+];
+const actual = [];
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ year: 1.7,
+ month: 1.7,
+ monthCode: "M01",
+ day: 1.7,
+ hour: 1.7,
+ minute: 1.7,
+ second: 1.7,
+ millisecond: 1.7,
+ microsecond: 1.7,
+ nanosecond: 1.7,
+ calendar: TemporalHelpers.calendarObserver(actual, "fields.calendar"),
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ overflow: "constrain",
+ extra: "property",
+}, "options");
+
+Temporal.PlainDateTime.from(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-default-constrain.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-default-constrain.js
new file mode 100644
index 0000000000..ba6b9725d3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-default-constrain.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: By default, overflow = constrain
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const date = {year: 2019, month: 1, day: 32};
+const data = [2019, 1, "M01", 31, 0, 0, 0, 0, 0, 0];
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from(date),
+ ...data,
+ "by default, overflow is constrain (overflow options argument absent)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from(date, {}),
+ ...data,
+ "by default, overflow is constrain (options argument is empty plain object)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from(date, () => {}),
+ ...data,
+ "by default, overflow is constrain (options argument is empty function)"
+);
+
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from(date, {overflow: "constrain"}),
+ ...data,
+ "by default, overflow is constrain (overflow options argument present)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-invalid-string.js
new file mode 100644
index 0000000000..cd735ddd64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-invalid-string.js
@@ -0,0 +1,49 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-totemporaldatetime steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ g. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ 3. Else,
+ a. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindatetime.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalDateTime(_item_, _options_).
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainDateTime(2000, 5, 2, 12),
+ new Temporal.PlainDate(2000, 5, 2),
+ new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"),
+ { year: 2000, month: 5, day: 2, hour: 12 },
+ "2000-05-02T12:00",
+];
+
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const value of validValues) {
+ for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from(value, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-reject.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-reject.js
new file mode 100644
index 0000000000..91f38ec3fe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-reject.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Possibly throw if overflow is reject
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from({year: 2019, month: 1, day: 31}, {overflow: "reject"}),
+ 2019, 1, "M01", 31, 0, 0, 0, 0, 0, 0,
+ "overflow reject, acceptable argument"
+);
+
+assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from({year: 2019, month: 1, day: 32}, {overflow: "reject"}),
+ "overflow reject, unacceptable argument"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-undefined.js
new file mode 100644
index 0000000000..0593ed9d41
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-undefined.js
@@ -0,0 +1,48 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-totemporaldatetime steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ g. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ 3. Else,
+ a. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindatetime.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalDateTime(_item_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainDateTime(2000, 5, 2, 12),
+ "2000-05-02T12:00",
+];
+validValues.forEach((value) => {
+ const explicit = Temporal.PlainDateTime.from(value, { overflow: undefined });
+ TemporalHelpers.assertPlainDateTime(explicit, 2000, 5, "M05", 2, 12, 0, 0, 0, 0, 0, "overflow is ignored");
+ const implicit = Temporal.PlainDateTime.from(value, {});
+ TemporalHelpers.assertPlainDateTime(implicit, 2000, 5, "M05", 2, 12, 0, 0, 0, 0, 0, "overflow is ignored");
+});
+
+const propertyBag = { year: 2000, month: 13, day: 34, hour: 12 };
+const explicit = Temporal.PlainDateTime.from(propertyBag, { overflow: undefined });
+TemporalHelpers.assertPlainDateTime(explicit, 2000, 12, "M12", 31, 12, 0, 0, 0, 0, 0, "default overflow is constrain");
+const implicit = Temporal.PlainDateTime.from(propertyBag, {});
+TemporalHelpers.assertPlainDateTime(implicit, 2000, 12, "M12", 31, 12, 0, 0, 0, 0, 0, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-wrong-type.js
new file mode 100644
index 0000000000..cadb84e952
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-wrong-type.js
@@ -0,0 +1,65 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-totemporaldatetime steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ g. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ 3. Else,
+ a. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindatetime.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalDateTime(_item_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainDateTime(2000, 5, 2, 12),
+ "2000-05-02T12:00",
+];
+validValues.forEach((value) => TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => Temporal.PlainDateTime.from(value, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 0, 0, 0, 0, 0, descr),
+));
+
+// See TemporalHelpers.checkStringOptionWrongType(); this code path has
+// different expectations for observable calls
+const propertyBag = { year: 2000, month: 5, day: 2, hour: 12 };
+
+assert.throws(RangeError, () => Temporal.PlainDateTime.from(propertyBag, { overflow: null }), "null");
+assert.throws(RangeError, () => Temporal.PlainDateTime.from(propertyBag, { overflow: true }), "true");
+assert.throws(RangeError, () => Temporal.PlainDateTime.from(propertyBag, { overflow: false }), "false");
+assert.throws(TypeError, () => Temporal.PlainDateTime.from(propertyBag, { overflow: Symbol() }), "symbol");
+assert.throws(RangeError, () => Temporal.PlainDateTime.from(propertyBag, { overflow: 2 }), "number");
+assert.throws(RangeError, () => Temporal.PlainDateTime.from(propertyBag, { overflow: 2n }), "bigint");
+assert.throws(RangeError, () => Temporal.PlainDateTime.from(propertyBag, { overflow: {} }), "plain object");
+
+// toString property should only be read and converted to a string once, because
+// a copied object with the resulting string on it is passed to
+// Calendar.dateFromFields().
+const expected = [
+ "get overflow.toString",
+ "call overflow.toString",
+];
+const actual = [];
+const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow");
+const result = Temporal.PlainDateTime.from(propertyBag, { overflow: observer });
+TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 0, 0, 0, 0, 0, "object with toString");
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/parser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/parser.js
new file mode 100644
index 0000000000..0ef6abb708
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/parser.js
@@ -0,0 +1,15 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainDateTime.from accepts a custom timezone that starts with "c".
+esid: sec-temporal.plaindatetime.from
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateTime = Temporal.PlainDateTime.from("2020-01-01T00:00:00+01:00[Custom]");
+TemporalHelpers.assertPlainDateTime(dateTime, 2020, 1, "M01", 1, 0, 0, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/prop-desc.js
new file mode 100644
index 0000000000..ff87eed71b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: The "from" property of Temporal.PlainDateTime
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.from,
+ "function",
+ "`typeof PlainDateTime.from` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime, "from", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..43930f216f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindatetime.prototype.from
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+
+assert.throws(RangeError, () => Temporal.PlainDateTime.from(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/read-time-fields-before-datefromfields.js
new file mode 100644
index 0000000000..2ef43274bb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/read-time-fields-before-datefromfields.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.plaindatetime.from step 3:
+ 3. Return ? ToTemporalDateTime(_item_, _options_).
+ sec-temporal-totemporaldatetime step 2.e:
+ e. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const datetime = Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321, calendar });
+
+assert.sameValue(datetime.hour, 12, "hour value");
+assert.sameValue(datetime.minute, 34, "minute value");
+assert.sameValue(datetime.second, 56, "second value");
+assert.sameValue(datetime.millisecond, 987, "millisecond value");
+assert.sameValue(datetime.microsecond, 654, "microsecond value");
+assert.sameValue(datetime.nanosecond, 321, "nanosecond value");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/subclassing-ignored.js
new file mode 100644
index 0000000000..8858428fc1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/subclassing-ignored.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: The receiver is never called when calling from()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.PlainDateTime,
+ "from",
+ ["2000-05-02T12:34:56.987654321"],
+ (result) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 321),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/year-zero.js
new file mode 100644
index 0000000000..60b900e160
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/year-zero.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-12-07",
+ "-000000-12-07T03:24:30",
+ "-000000-12-07T03:24:30+01:00",
+ "-000000-12-07T03:24:30+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/hour-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/hour-undefined.js
new file mode 100644
index 0000000000..468a88729b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/hour-undefined.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Hour argument defaults to 0 if not given
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const args = [2000, 5, 2];
+
+TemporalHelpers.assertPlainDateTime(
+ new Temporal.PlainDateTime(...args, undefined),
+ 2000, 5, "M05", 2, 0, 0, 0, 0, 0, 0,
+ "hour default argument (argument present)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ new Temporal.PlainDateTime(...args),
+ 2000, 5, "M05", 2, 0, 0, 0, 0, 0, 0,
+ "hour default argument (argument missing)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..86bdb7ec8b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/infinity-throws-rangeerror.js
@@ -0,0 +1,165 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainDateTime throws a RangeError if any value is Infinity
+esid: sec-temporal.plaindatetime
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => new Temporal.PlainDateTime(Infinity, 1, 1));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, Infinity, 1));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, 0, Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite year",
+ [O(Infinity, "year"), O(1, "month"), O(1, "day")],
+ ["get year.valueOf", "call year.valueOf"
+ ]
+ ],
+ [
+ "infinite month",
+ [O(2, "year"), O(Infinity, "month"), O(1, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf"
+ ]
+ ],
+ [
+ "infinite day",
+ [O(2, "year"), O(1, "month"), O(Infinity, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf", "get day.valueOf", "call day.valueOf"
+ ]
+ ],
+ [
+ "infinite hour",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(Infinity, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf"
+ ]
+ ],
+ [
+ "infinite minute",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(Infinity, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf"
+ ]
+ ],
+ [
+ "infinite second",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(1, "minute"), O(Infinity, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get second.valueOf",
+ "call second.valueOf"
+ ]
+ ],
+ [
+ "infinite millisecond",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(1, "minute"), O(1, "second"), O(Infinity, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get second.valueOf",
+ "call second.valueOf",
+ "get millisecond.valueOf",
+ "call millisecond.valueOf"
+ ]
+ ],
+ [
+ "infinite microsecond",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(Infinity, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get second.valueOf",
+ "call second.valueOf",
+ "get millisecond.valueOf",
+ "call millisecond.valueOf",
+ "get microsecond.valueOf",
+ "call microsecond.valueOf"
+ ]
+ ],
+ [
+ "infinite nanosecond",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(Infinity, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get second.valueOf",
+ "call second.valueOf",
+ "get millisecond.valueOf",
+ "call millisecond.valueOf",
+ "get microsecond.valueOf",
+ "call microsecond.valueOf",
+ "get nanosecond.valueOf",
+ "call nanosecond.valueOf"
+ ]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.PlainDateTime(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/length.js
new file mode 100644
index 0000000000..af987a5cf0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Temporal.PlainDateTime.length is 3
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime, "length", {
+ value: 3,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/limits.js
new file mode 100644
index 0000000000..a02ad333df
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/limits.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Checking limits of representable PlainDateTime
+features: [Temporal]
+---*/
+
+assert.throws(
+ RangeError,
+ () => new Temporal.PlainDateTime(-271821, 4, 19, 0, 0, 0, 0, 0, 0),
+ "negative year out of bounds"
+);
+assert.throws(
+ RangeError,
+ () => new Temporal.PlainDateTime(275760, 9, 14, 0, 0, 0, 0, 0, 0),
+ "positive year out of bounds"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/microsecond-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/microsecond-undefined.js
new file mode 100644
index 0000000000..84238ad1db
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/microsecond-undefined.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Microsecond argument defaults to 0 if not given
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const args = [2000, 5, 2, 12, 34, 56, 123];
+
+TemporalHelpers.assertPlainDateTime(
+ new Temporal.PlainDateTime(...args, undefined),
+ 2000, 5, "M05", 2, 12, 34, 56, 123, 0, 0,
+ "microsecond default argument (argument present)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ new Temporal.PlainDateTime(...args),
+ 2000, 5, "M05", 2, 12, 34, 56, 123, 0, 0,
+ "microsecond default argument (argument missing)"
+);
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/millisecond-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/millisecond-undefined.js
new file mode 100644
index 0000000000..4b36f39d5f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/millisecond-undefined.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Millisecond argument defaults to 0 if not given
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const args = [2000, 5, 2, 12, 34, 56];
+
+TemporalHelpers.assertPlainDateTime(
+ new Temporal.PlainDateTime(...args, undefined),
+ 2000, 5, "M05", 2, 12, 34, 56, 0, 0, 0,
+ "millisecond default argument (argument present)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ new Temporal.PlainDateTime(...args),
+ 2000, 5, "M05", 2, 12, 34, 56, 0, 0, 0,
+ "millisecond default argument (argument missing)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/minute-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/minute-undefined.js
new file mode 100644
index 0000000000..ac6cfa5869
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/minute-undefined.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Minute argument defaults to 0 if not given
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const args = [2000, 5, 2, 12];
+
+TemporalHelpers.assertPlainDateTime(
+ new Temporal.PlainDateTime(...args, undefined),
+ 2000, 5, "M05", 2, 12, 0, 0, 0, 0, 0,
+ "minute default argument (argument present)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ new Temporal.PlainDateTime(...args),
+ 2000, 5, "M05", 2, 12, 0, 0, 0, 0, 0,
+ "minute default argument (argument missing)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/missing-arguments.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/missing-arguments.js
new file mode 100644
index 0000000000..a87d6ebe4c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/missing-arguments.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: RangeError thrown after processing given args when invoked without all required args
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "valueOf year",
+ "valueOf month",
+];
+const actual = [];
+const args = [
+ { valueOf() { actual.push("valueOf year"); return 1; } },
+ { valueOf() { actual.push("valueOf month"); return 1; } },
+];
+
+assert.throws(RangeError, () => new Temporal.PlainDateTime(...args));
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/name.js
new file mode 100644
index 0000000000..0ba2c7917d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Temporal.PlainDateTime.name is "PlainDateTime"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime, "name", {
+ value: "PlainDateTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/nanosecond-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/nanosecond-undefined.js
new file mode 100644
index 0000000000..89d9061c78
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/nanosecond-undefined.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Nanosecond argument defaults to 0 if not given
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const args = [2000, 5, 2, 12, 34, 56, 123, 456];
+
+TemporalHelpers.assertPlainDateTime(
+ new Temporal.PlainDateTime(...args, undefined),
+ 2000, 5, "M05", 2, 12, 34, 56, 123, 456, 0,
+ "nanosecond default argument (argument present)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ new Temporal.PlainDateTime(...args),
+ 2000, 5, "M05", 2, 12, 34, 56, 123, 456, 0,
+ "nanosecond default argument (argument missing)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..938d56bd5c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,165 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainDateTime throws a RangeError if any value is -Infinity
+esid: sec-temporal.plaindatetime
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => new Temporal.PlainDateTime(-Infinity, 1, 1));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, -Infinity, 1));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, 0, -Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite year",
+ [O(-Infinity, "year"), O(1, "month"), O(1, "day")],
+ ["get year.valueOf", "call year.valueOf"
+ ]
+ ],
+ [
+ "infinite month",
+ [O(2, "year"), O(-Infinity, "month"), O(1, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf"
+ ]
+ ],
+ [
+ "infinite day",
+ [O(2, "year"), O(1, "month"), O(-Infinity, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf", "get day.valueOf", "call day.valueOf"
+ ]
+ ],
+ [
+ "infinite hour",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(-Infinity, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf"
+ ]
+ ],
+ [
+ "infinite minute",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(-Infinity, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf"
+ ]
+ ],
+ [
+ "infinite second",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(1, "minute"), O(-Infinity, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get second.valueOf",
+ "call second.valueOf"
+ ]
+ ],
+ [
+ "infinite millisecond",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(1, "minute"), O(1, "second"), O(-Infinity, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get second.valueOf",
+ "call second.valueOf",
+ "get millisecond.valueOf",
+ "call millisecond.valueOf"
+ ]
+ ],
+ [
+ "infinite microsecond",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(-Infinity, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get second.valueOf",
+ "call second.valueOf",
+ "get millisecond.valueOf",
+ "call millisecond.valueOf",
+ "get microsecond.valueOf",
+ "call microsecond.valueOf"
+ ]
+ ],
+ [
+ "infinite nanosecond",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(-Infinity, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get second.valueOf",
+ "call second.valueOf",
+ "get millisecond.valueOf",
+ "call millisecond.valueOf",
+ "get microsecond.valueOf",
+ "call microsecond.valueOf",
+ "get nanosecond.valueOf",
+ "call nanosecond.valueOf"
+ ]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.PlainDateTime(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/order-of-operations.js
new file mode 100644
index 0000000000..ce518f86f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/order-of-operations.js
@@ -0,0 +1,42 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Arguments are converted to primitives in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "get (argument 0).valueOf",
+ "call (argument 0).valueOf",
+ "get (argument 1).valueOf",
+ "call (argument 1).valueOf",
+ "get (argument 2).valueOf",
+ "call (argument 2).valueOf",
+ "get (argument 3).valueOf",
+ "call (argument 3).valueOf",
+ "get (argument 4).valueOf",
+ "call (argument 4).valueOf",
+ "get (argument 5).valueOf",
+ "call (argument 5).valueOf",
+ "get (argument 6).valueOf",
+ "call (argument 6).valueOf",
+ "get (argument 7).valueOf",
+ "call (argument 7).valueOf",
+ "get (argument 8).valueOf",
+ "call (argument 8).valueOf",
+];
+
+const dateTimeArgs = [2020, 12, 24, 12, 34, 56, 123, 456, 789].map((value, idx) =>
+ TemporalHelpers.toPrimitiveObserver(actual, value, `(argument ${idx})`));
+
+const dateTime = new Temporal.PlainDateTime(...dateTimeArgs, "iso8601");
+assert.compareArray(actual, expected);
+
+TemporalHelpers.assertPlainDateTime(dateTime, 2020, 12, "M12", 24, 12, 34, 56, 123, 456, 789);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prop-desc.js
new file mode 100644
index 0000000000..f595d1e968
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: The "PlainDateTime" property of Temporal
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime,
+ "function",
+ "`typeof PlainDateTime` is `function`"
+);
+
+verifyProperty(Temporal, "PlainDateTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/ambiguous-date.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/ambiguous-date.js
new file mode 100644
index 0000000000..d05823e739
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/ambiguous-date.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Ambiguous addition is handled according to the overflow option
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const jan31 = new Temporal.PlainDateTime(2020, 1, 31, 15, 0);
+
+TemporalHelpers.assertPlainDateTime(
+ jan31.add({ months: 1 }),
+ 2020, 2, "M02", 29, 15, 0, 0, 0, 0, 0,
+ "constrain when ambiguous result (overflow options not supplied)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ jan31.add({ months: 1 }, { overflow: "constrain" }),
+ 2020, 2, "M02", 29, 15, 0, 0, 0, 0, 0,
+ "constrain when ambiguous result (overflow options supplied)"
+);
+
+assert.throws(
+ RangeError,
+ () => jan31.add({ months: 1 }, { overflow: "reject" }),
+ "throw when ambiguous result with reject"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-duration-max.js
new file mode 100644
index 0000000000..9093b72e2d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-duration-max.js
@@ -0,0 +1,58 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Maximum allowed duration
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1970, 1, 1);
+
+const maxCases = [
+ ["P273790Y8M12DT23H59M59.999999999S", "string with max years"],
+ [{ years: 273790, months: 8, days: 12, nanoseconds: 86399999999999 }, "property bag with max years"],
+ ["P3285488M12DT23H59M59.999999999S", "string with max months"],
+ [{ months: 3285488, days: 12, nanoseconds: 86399999999999 }, "property bag with max months"],
+ ["P14285714W2DT23H59M59.999999999S", "string with max weeks"],
+ [{ weeks: 14285714, days: 2, nanoseconds: 86399999999999 }, "property bag with max weeks"],
+ ["P100000000DT23H59M59.999999999S", "string with max days"],
+ [{ days: 100000000, nanoseconds: 86399999999999 }, "property bag with max days"],
+ ["PT2400000023H59M59.999999999S", "string with max hours"],
+ [{ hours: 2400000023, nanoseconds: 3599999999999 }, "property bag with max hours"],
+ ["PT144000001439M59.999999999S", "string with max minutes"],
+ [{ minutes: 144000001439, nanoseconds: 59999999999 }, "property bag with max minutes"],
+ ["PT8640000086399.999999999S", "string with max seconds"],
+ [{ seconds: 8640000086399, nanoseconds: 999999999 }, "property bag with max seconds"],
+];
+
+for (const [arg, descr] of maxCases) {
+ const result = instance.add(arg);
+ TemporalHelpers.assertPlainDateTime(result, 275760, 9, "M09", 13, 23, 59, 59, 999, 999, 999, `operation succeeds with ${descr}`);
+}
+
+const minCases = [
+ ["-P273790Y8M11DT23H59M59.999999999S", "string with min years"],
+ [{ years: -273790, months: -8, days: -11, nanoseconds: -86399999999999 }, "property bag with min years"],
+ ["-P3285488M11DT23H59M59.999999999S", "string with min months"],
+ [{ months: -3285488, days: -11, nanoseconds: -86399999999999 }, "property bag with min months"],
+ ["-P14285714W2DT23H59M59.999999999S", "string with min weeks"],
+ [{ weeks: -14285714, days: -2, nanoseconds: -86399999999999 }, "property bag with min weeks"],
+ ["-P100000000DT23H59M59.999999999S", "string with min days"],
+ [{ days: -100000000, nanoseconds: -86399999999999 }, "property bag with min days"],
+ ["-PT2400000023H59M59.999999999S", "string with min hours"],
+ [{ hours: -2400000023, nanoseconds: -3599999999999 }, "property bag with min hours"],
+ ["-PT144000001439M59.999999999S", "string with min minutes"],
+ [{ minutes: -144000001439, nanoseconds: -59999999999 }, "property bag with min minutes"],
+ ["-PT8640000086399.999999999S", "string with min seconds"],
+ [{ seconds: -8640000086399, nanoseconds: -999999999 }, "property bag with min seconds"],
+];
+
+for (const [arg, descr] of minCases) {
+ const result = instance.add(arg);
+ TemporalHelpers.assertPlainDateTime(result, -271821, 4, "M04", 19, 0, 0, 0, 0, 0, 1, `operation succeeds with ${descr}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-duration-out-of-range.js
new file mode 100644
index 0000000000..19e7edbb84
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-duration-out-of-range.js
@@ -0,0 +1,75 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Duration-like argument that is out of range
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1970, 1, 1);
+
+const cases = [
+ // 2^32 = 4294967296
+ ["P4294967296Y", "string with years > max"],
+ [{ years: 4294967296 }, "property bag with years > max"],
+ ["-P4294967296Y", "string with years < min"],
+ [{ years: -4294967296 }, "property bag with years < min"],
+ ["P4294967296M", "string with months > max"],
+ [{ months: 4294967296 }, "property bag with months > max"],
+ ["-P4294967296M", "string with months < min"],
+ [{ months: -4294967296 }, "property bag with months < min"],
+ ["P4294967296W", "string with weeks > max"],
+ [{ weeks: 4294967296 }, "property bag with weeks > max"],
+ ["-P4294967296W", "string with weeks < min"],
+ [{ weeks: -4294967296 }, "property bag with weeks < min"],
+
+ // ceil(max safe integer / 86400) = 104249991375
+ ["P104249991375D", "string with days > max"],
+ [{ days: 104249991375 }, "property bag with days > max"],
+ ["P104249991374DT24H", "string where hours balance into days > max"],
+ [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"],
+ ["-P104249991375D", "string with days < min"],
+ [{ days: -104249991375 }, "property bag with days < min"],
+ ["-P104249991374DT24H", "string where hours balance into days < min"],
+ [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"],
+
+ // ceil(max safe integer / 3600) = 2501999792984
+ ["PT2501999792984H", "string with hours > max"],
+ [{ hours: 2501999792984 }, "property bag with hours > max"],
+ ["PT2501999792983H60M", "string where minutes balance into hours > max"],
+ [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"],
+ ["-PT2501999792984H", "string with hours < min"],
+ [{ hours: -2501999792984 }, "property bag with hours < min"],
+ ["-PT2501999792983H60M", "string where minutes balance into hours < min"],
+ [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"],
+
+ // ceil(max safe integer / 60) = 150119987579017
+ ["PT150119987579017M", "string with minutes > max"],
+ [{ minutes: 150119987579017 }, "property bag with minutes > max"],
+ ["PT150119987579016M60S", "string where seconds balance into minutes > max"],
+ [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"],
+ ["-PT150119987579017M", "string with minutes < min"],
+ [{ minutes: -150119987579017 }, "property bag with minutes < min"],
+ ["-PT150119987579016M60S", "string where seconds balance into minutes < min"],
+ [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"],
+
+ // 2^53 = 9007199254740992
+ ["PT9007199254740992S", "string with seconds > max"],
+ [{ seconds: 9007199254740992 }, "property bag with seconds > max"],
+ [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"],
+ ["-PT9007199254740992S", "string with seconds < min"],
+ [{ seconds: -9007199254740992 }, "property bag with seconds < min"],
+ [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"],
+];
+
+for (const [arg, descr] of cases) {
+ assert.throws(RangeError, () => instance.add(arg), `${descr} is out of range`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-duration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-duration.js
new file mode 100644
index 0000000000..0a7bab206c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-duration.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Duration object arguments are handled
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const jan31 = new Temporal.PlainDateTime(2020, 1, 31, 15, 0);
+
+TemporalHelpers.assertPlainDateTime(
+ jan31.add(Temporal.Duration.from("P1MT1S")),
+ 2020, 2, "M02", 29, 15, 0, 1, 0, 0, 0,
+ "Duration argument"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-invalid-property.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-invalid-property.js
new file mode 100644
index 0000000000..19f1fe193a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-invalid-property.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: temporalDurationLike object must contain at least one correctly spelled property
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+
+assert.throws(
+ TypeError,
+ () => instance.add({}),
+ "Throws TypeError if no property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.add({ nonsense: true }),
+ "Throws TypeError if no recognized property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.add({ sign: 1 }),
+ "Sign property is not recognized"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-mixed-sign.js
new file mode 100644
index 0000000000..c2d45bd77d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-mixed-sign.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Positive and negative values in the temporalDurationLike argument are not acceptable
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+
+["constrain", "reject"].forEach((overflow) => {
+ assert.throws(
+ RangeError,
+ () => instance.add({ hours: 1, minutes: -30 }, { overflow }),
+ `mixed positive and negative values always throw (overflow = "${overflow}")`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-not-object.js
new file mode 100644
index 0000000000..cd5eb71e7d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-not-object.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Passing a primitive other than string to add() throws
+features: [Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+assert.throws(TypeError, () => instance.add(undefined), "undefined");
+assert.throws(TypeError, () => instance.add(null), "null");
+assert.throws(TypeError, () => instance.add(true), "boolean");
+assert.throws(RangeError, () => instance.add(""), "empty string");
+assert.throws(TypeError, () => instance.add(Symbol()), "Symbol");
+assert.throws(TypeError, () => instance.add(7), "number");
+assert.throws(TypeError, () => instance.add(7n), "bigint");
+assert.throws(TypeError, () => instance.add([]), "array");
+assert.throws(TypeError, () => instance.add(() => {}), "function");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-singular-properties.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-singular-properties.js
new file mode 100644
index 0000000000..9ceaeb21c3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-singular-properties.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Singular properties in the property bag are always ignored
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+
+[
+ { year: 1 },
+ { month: 2 },
+ { week: 3 },
+ { day: 4 },
+ { hour: 5 },
+ { minute: 6 },
+ { second: 7 },
+ { millisecond: 8 },
+ { microsecond: 9 },
+ { nanosecond: 10 },
+].forEach((badObject) => {
+ assert.throws(TypeError, () => instance.add(badObject),
+ "Throw TypeError if temporalDurationLike is not valid");
+});
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-string-fractional-units-rounding-mode.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-string-fractional-units-rounding-mode.js
new file mode 100644
index 0000000000..cd03de8f67
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-string-fractional-units-rounding-mode.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Strings with fractional duration units are rounded with the correct rounding mode
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2);
+
+TemporalHelpers.assertPlainDateTime(datetime.add("PT1.03125H"), 2000, 5, "M05", 2, 1, 1, 52, 500, 0, 0,
+ "positive fractional units rounded with correct rounding mode");
+TemporalHelpers.assertPlainDateTime(datetime.add("-PT1.03125H"), 2000, 5, "M05", 1, 22, 58, 7, 500, 0, 0,
+ "negative fractional units rounded with correct rounding mode");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-string-negative-fractional-units.js
new file mode 100644
index 0000000000..56f8979c29
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-string-negative-fractional-units.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+
+const resultHours = instance.add("-PT24.567890123H");
+TemporalHelpers.assertPlainDateTime(resultHours, 2000, 4, "M04", 30, 23, 25, 55, 595, 557, 200, "negative fractional hours");
+
+const resultMinutes = instance.add("-PT1440.567890123M");
+TemporalHelpers.assertPlainDateTime(resultMinutes, 2000, 4, "M04", 30, 23, 59, 25, 926, 592, 620, "negative fractional minutes");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-string.js
new file mode 100644
index 0000000000..e2cd1bde41
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-string.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: A string is parsed into the correct object when passed as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+const result = instance.add("P3D");
+TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 5, 0, 34, 56, 987, 654, 321);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/balance-negative-time-units.js
new file mode 100644
index 0000000000..cfc9691634
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/balance-negative-time-units.js
@@ -0,0 +1,52 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-addtime step 8:
+ 8. Return ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-adddatetime step 1:
+ 1. Let _timeResult_ be ? AddTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
+ sec-temporal.plaindatetime.prototype.add step 5:
+ 5. Let _result_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], _duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1996, 5, 2, 1, 1, 1, 1, 1, 1);
+
+const result1 = datetime.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainDateTime(result1, 1996, 5, "M05", 2, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
+
+const result2 = datetime.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainDateTime(result2, 1996, 5, "M05", 2, 1, 1, 1, 0, 999, 1, "microseconds balance");
+
+const result3 = datetime.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainDateTime(result3, 1996, 5, "M05", 2, 1, 1, 0, 999, 1, 1, "milliseconds balance");
+
+const result4 = datetime.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainDateTime(result4, 1996, 5, "M05", 2, 1, 0, 59, 1, 1, 1, "seconds balance");
+
+const result5 = datetime.add(new Temporal.Duration(0, 0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainDateTime(result5, 1996, 5, "M05", 2, 0, 59, 1, 1, 1, 1, "minutes balance");
+
+const result6 = datetime.add(new Temporal.Duration(0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainDateTime(result6, 1996, 5, "M05", 1, 23, 1, 1, 1, 1, 1, "hours balance");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/branding.js
new file mode 100644
index 0000000000..7c97373b75
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const add = Temporal.PlainDateTime.prototype.add;
+
+assert.sameValue(typeof add, "function");
+
+const args = [new Temporal.Duration(5)];
+
+assert.throws(TypeError, () => add.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => add.apply(null, args), "null");
+assert.throws(TypeError, () => add.apply(true, args), "true");
+assert.throws(TypeError, () => add.apply("", args), "empty string");
+assert.throws(TypeError, () => add.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => add.apply(1, args), "1");
+assert.throws(TypeError, () => add.apply({}, args), "plain object");
+assert.throws(TypeError, () => add.apply(Temporal.PlainDateTime, args), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => add.apply(Temporal.PlainDateTime.prototype, args), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..90cd85ab31
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateAddOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateAdd");
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateAdd should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.add(new Temporal.Duration(1));
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", dateAddOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/builtin.js
new file mode 100644
index 0000000000..b095e5d926
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: >
+ Tests that Temporal.PlainDateTime.prototype.add
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.add),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.add),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.add),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.add.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/calendar-dateadd.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/calendar-dateadd.js
new file mode 100644
index 0000000000..5870fac724
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/calendar-dateadd.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: PlainDateTime.prototype.add should call dateAdd with the appropriate values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(plainDate, duration, options) {
+ ++calls;
+ TemporalHelpers.assertPlainDate(plainDate, 2020, 3, "M03", 14, "plainDate argument");
+ TemporalHelpers.assertDuration(duration, 0, 10, 0, 1, 0, 0, 0, 0, 0, 0, "duration argument");
+ assert.sameValue(typeof options, "object", "options argument: type");
+ assert.sameValue(Object.getPrototypeOf(options), null, "options argument: prototype");
+ return super.dateAdd(plainDate, duration, options);
+ }
+}
+
+const plainDateTime = new Temporal.PlainDateTime(2020, 3, 14, 12, 34, 56, 987, 654, 321, new CustomCalendar());
+const result = plainDateTime.add({ months: 10, hours: 14 });
+TemporalHelpers.assertPlainDateTime(result, 2021, 1, "M01", 15, 2, 34, 56, 987, 654, 321);
+assert.sameValue(calls, 1, "should have called dateAdd");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/hour-overflow.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/hour-overflow.js
new file mode 100644
index 0000000000..4d6b28f57e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/hour-overflow.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Testing overflow hours (adding hours that push one to the next/previous day)
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2020, 5, 31, 23, 12, 38, 271, 986, 102);
+
+TemporalHelpers.assertPlainDateTime(
+ earlier.add({ hours: 2 }),
+ 2020, 6, "M06", 1, 1, 12, 38, 271, 986, 102,
+ "hours overflow (push to next day)"
+);
+
+const later = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102);
+
+TemporalHelpers.assertPlainDateTime(
+ later.add({ hours: -12 }),
+ 2019, 10, "M10", 28, 22, 46, 38, 271, 986, 102,
+ "hours overflow (push to previous day)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..d9e9e8ce14
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainDateTime.prototype.add throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.plaindatetime.prototype.add
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/length.js
new file mode 100644
index 0000000000..97c4956693
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Temporal.PlainDateTime.prototype.add.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.add, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/limits.js
new file mode 100644
index 0000000000..6c9178f9e3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/limits.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Checking limits of representable PlainDateTime
+features: [Temporal]
+---*/
+
+const min = new Temporal.PlainDateTime(-271821, 4, 19, 0, 0, 0, 0, 0, 1);
+const max = new Temporal.PlainDateTime(275760, 9, 13, 23, 59, 59, 999, 999, 999);
+
+["reject", "constrain"].forEach((overflow) => {
+ assert.throws(
+ RangeError,
+ () => max.add({ nanoseconds: 1 }, { overflow }),
+ `adding 1 nanosecond beyond maximum limit (overflow = ${overflow})`
+ );
+ assert.throws(
+ RangeError,
+ () => min.add({ nanoseconds: -1 }, { overflow }),
+ `adding -1 nanosecond beyond minimum limit (overflow = ${overflow})`
+ );
+
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/name.js
new file mode 100644
index 0000000000..5e3bad4a9c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Temporal.PlainDateTime.prototype.add.name is "add".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.add, "name", {
+ value: "add",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/negative-duration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/negative-duration.js
new file mode 100644
index 0000000000..826224ef34
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/negative-duration.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Negative durations can be supplied
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const jan31 = new Temporal.PlainDateTime(2020, 1, 31, 15, 0);
+
+TemporalHelpers.assertPlainDateTime(
+ jan31.add({ minutes: -30 }),
+ 2020, 1, "M01", 31, 14, 30, 0, 0, 0, 0,
+ "negative minutes"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ jan31.add({ seconds: -30 }),
+ 2020, 1, "M01", 31, 14, 59, 30, 0, 0, 0,
+ "negative seconds"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..97b08ed520
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainDateTime.prototype.add throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.plaindatetime.prototype.add
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: -Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/non-integer-throws-rangeerror.js
new file mode 100644
index 0000000000..24ce0d5391
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/non-integer-throws-rangeerror.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.add({ [field]: -1.5 }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/not-a-constructor.js
new file mode 100644
index 0000000000..13c3ca718d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: >
+ Temporal.PlainDateTime.prototype.add does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.add();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.add), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.add)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-empty.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-empty.js
new file mode 100644
index 0000000000..e45efaaaae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-empty.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const jan31 = new Temporal.PlainDateTime(2020, 1, 31, 15, 0);
+
+TemporalHelpers.assertPlainDateTime(
+ jan31.add({ months: 1 }, {}),
+ 2020, 2, "M02", 29, 15, 0, 0, 0, 0, 0,
+ "options may be empty object"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ jan31.add({ months: 1 }, () => {}),
+ 2020, 2, "M02", 29, 15, 0, 0, 0, 0, 0,
+ "options may be function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-invalid.js
new file mode 100644
index 0000000000..31a98f2787
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-invalid.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Various invalid (wrong type) values for options argument
+features: [Temporal, Symbol]
+---*/
+
+const jan31 = new Temporal.PlainDateTime(2020, 1, 31, 15, 0);
+
+const badOptions = [null, 1, 'hello', true, Symbol('foo'), 1n];
+
+badOptions.forEach((bad) => {
+ assert.throws(
+ TypeError,
+ () => jan31.add({ years: 1 }, bad),
+ `invalid options (${typeof bad})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-undefined.js
new file mode 100644
index 0000000000..410972a985
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-undefined.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 1, 31, 12, 34, 56, 987, 654, 321);
+const duration = { months: 1 };
+
+const explicit = datetime.add(duration, undefined);
+assert.sameValue(explicit.month, 2, "default overflow is constrain");
+assert.sameValue(explicit.day, 29, "default overflow is constrain");
+
+const implicit = datetime.add(duration);
+assert.sameValue(implicit.month, 2, "default overflow is constrain");
+assert.sameValue(implicit.day, 29, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-wrong-type.js
new file mode 100644
index 0000000000..30da2b6095
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-wrong-type.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.add({ months: 1 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/order-of-operations.js
new file mode 100644
index 0000000000..11024f653b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/order-of-operations.js
@@ -0,0 +1,127 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Properties on an object passed to add() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalDurationRecord
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.months.valueOf",
+ "call fields.months.valueOf",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.weeks.valueOf",
+ "call fields.weeks.valueOf",
+ "get fields.years",
+ "get fields.years.valueOf",
+ "call fields.years.valueOf",
+ // AddDateTime -> AddDate
+ "get this.calendar.dateAdd",
+ "call this.calendar.dateAdd",
+ // inside Calendar.p.dateAdd
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options");
+
+instance.add(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+actual.splice(0); // clear
+
+const noCalendarExpected = [
+ // ToTemporalDurationRecord
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.years",
+ "get this.calendar.dateAdd",
+ // AddDateTime -> AddDate
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+
+const noCalendarFields = TemporalHelpers.propertyBagObserver(actual, {
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+instance.add(noCalendarFields, options);
+assert.compareArray(actual, noCalendarExpected, "order of operations with no calendar operation");
+
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/overflow-invalid-string.js
new file mode 100644
index 0000000000..e45665d393
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/overflow-invalid-string.js
@@ -0,0 +1,35 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-adddatetime step 4:
+ 4. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.plaindatetime.prototype.add step 5:
+ 5. Let _result_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], _duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12);
+const duration = new Temporal.Duration(3, 3, 0, 3, 3);
+
+
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => datetime.add({ months: 1 }, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/overflow-undefined.js
new file mode 100644
index 0000000000..d874734c28
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/overflow-undefined.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-adddatetime step 4:
+ 4. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.plaindatetime.prototype.add step 5:
+ 5. Let _result_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], _duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 31, 12);
+const duration = new Temporal.Duration(3, 1);
+
+const explicit = datetime.add(duration, { overflow: undefined });
+TemporalHelpers.assertPlainDateTime(explicit, 2003, 6, "M06", 30, 12, 0, 0, 0, 0, 0, "default overflow is constrain");
+const implicit = datetime.add(duration, {});
+TemporalHelpers.assertPlainDateTime(implicit, 2003, 6, "M06", 30, 12, 0, 0, 0, 0, 0, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/overflow-wrong-type.js
new file mode 100644
index 0000000000..f9be928b8d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/overflow-wrong-type.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-adddatetime step 4:
+ 4. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.plaindatetime.prototype.add step 5:
+ 5. Let _result_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], _duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12);
+const duration = new Temporal.Duration(3, 3, 0, 3, 3);
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => datetime.add(duration, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainDateTime(result, 2003, 8, "M08", 5, 15, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/prop-desc.js
new file mode 100644
index 0000000000..6934b827f9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: The "add" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.add,
+ "function",
+ "`typeof PlainDateTime.prototype.add` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "add", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/subclassing-ignored.js
new file mode 100644
index 0000000000..8e42154531
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/subclassing-ignored.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Objects of a subclass are never created as return values for add()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDateTime,
+ [2000, 5, 2, 12, 34, 56, 987, 654, 321],
+ "add",
+ [{ nanoseconds: 1 }],
+ (result) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 322),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/branding.js
new file mode 100644
index 0000000000..f6208fa688
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.calendarid
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const calendarId = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "calendarId").get;
+
+assert.sameValue(typeof calendarId, "function");
+
+assert.throws(TypeError, () => calendarId.call(undefined), "undefined");
+assert.throws(TypeError, () => calendarId.call(null), "null");
+assert.throws(TypeError, () => calendarId.call(true), "true");
+assert.throws(TypeError, () => calendarId.call(""), "empty string");
+assert.throws(TypeError, () => calendarId.call(Symbol()), "symbol");
+assert.throws(TypeError, () => calendarId.call(1), "1");
+assert.throws(TypeError, () => calendarId.call({}), "plain object");
+assert.throws(TypeError, () => calendarId.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => calendarId.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..51cdaea3c3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.calendarid
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.calendarId;
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/prop-desc.js
new file mode 100644
index 0000000000..7b190782ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.calendarid
+description: The "calendarId" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "calendarId");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/constructor.js
new file mode 100644
index 0000000000..f9ba1858d6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/constructor.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.constructor
+description: Test for Temporal.PlainDateTime.prototype.constructor.
+info: The initial value of Temporal.PlainDateTime.prototype.constructor is %Temporal.PlainDateTime%.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype, "constructor", {
+ value: Temporal.PlainDateTime,
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/branding.js
new file mode 100644
index 0000000000..ecf7156ae5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.day
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const day = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "day").get;
+
+assert.sameValue(typeof day, "function");
+
+assert.throws(TypeError, () => day.call(undefined), "undefined");
+assert.throws(TypeError, () => day.call(null), "null");
+assert.throws(TypeError, () => day.call(true), "true");
+assert.throws(TypeError, () => day.call(""), "empty string");
+assert.throws(TypeError, () => day.call(Symbol()), "symbol");
+assert.throws(TypeError, () => day.call(1), "1");
+assert.throws(TypeError, () => day.call({}), "plain object");
+assert.throws(TypeError, () => day.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => day.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..d2a691a269
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.day
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dayOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "day");
+Object.defineProperty(Temporal.Calendar.prototype, "day", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("day should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.day;
+
+Object.defineProperty(Temporal.Calendar.prototype, "day", dayOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/custom.js
new file mode 100644
index 0000000000..c06146b056
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.day
+description: Custom calendar tests for day().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ day(...args) {
+ ++calls;
+ assert.compareArray(args, [pdt], "day arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pdt = new Temporal.PlainDateTime(1830, 8, 25, 20, 0, 0, 0, 0, 0, calendar);
+const result = pdt.day;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/prop-desc.js
new file mode 100644
index 0000000000..d2a6f079e7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.day
+description: The "day" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "day");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/validate-calendar-value.js
new file mode 100644
index 0000000000..f029012c5d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/validate-calendar-value.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.day
+description: Validate result returned from calendar day() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ day() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.throws(error, () => instance.day, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/basic.js
new file mode 100644
index 0000000000..29c5ae481c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/basic.js
@@ -0,0 +1,15 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.dayofweek
+description: Checking day of week for a "normal" case (non-undefined, non-boundary case, etc.)
+features: [Temporal]
+---*/
+
+const calendar = Temporal.Calendar.from("iso8601");
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar);
+assert.sameValue(datetime.dayOfWeek, 4, "check day of week information");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/branding.js
new file mode 100644
index 0000000000..ff2d862576
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.dayofweek
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const dayOfWeek = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "dayOfWeek").get;
+
+assert.sameValue(typeof dayOfWeek, "function");
+
+assert.throws(TypeError, () => dayOfWeek.call(undefined), "undefined");
+assert.throws(TypeError, () => dayOfWeek.call(null), "null");
+assert.throws(TypeError, () => dayOfWeek.call(true), "true");
+assert.throws(TypeError, () => dayOfWeek.call(""), "empty string");
+assert.throws(TypeError, () => dayOfWeek.call(Symbol()), "symbol");
+assert.throws(TypeError, () => dayOfWeek.call(1), "1");
+assert.throws(TypeError, () => dayOfWeek.call({}), "plain object");
+assert.throws(TypeError, () => dayOfWeek.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => dayOfWeek.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..670601adc2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.dayofweek
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dayOfWeekOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dayOfWeek");
+Object.defineProperty(Temporal.Calendar.prototype, "dayOfWeek", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dayOfWeek should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.dayOfWeek;
+
+Object.defineProperty(Temporal.Calendar.prototype, "dayOfWeek", dayOfWeekOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/custom.js
new file mode 100644
index 0000000000..4bb559fd3f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.dayofweek
+description: Custom calendar tests for dayOfWeek().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dayOfWeek(...args) {
+ ++calls;
+ assert.compareArray(args, [pdt], "dayOfWeek arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pdt = new Temporal.PlainDateTime(1830, 8, 25, 20, 0, 0, 0, 0, 0, calendar);
+const result = pdt.dayOfWeek;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/prop-desc.js
new file mode 100644
index 0000000000..8045da6228
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.dayofweek
+description: The "dayOfWeek" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "dayOfWeek");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/validate-calendar-value.js
new file mode 100644
index 0000000000..2612ef18f0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/validate-calendar-value.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.dayofweek
+description: Validate result returned from calendar dayOfWeek() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ dayOfWeek() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.throws(error, () => instance.dayOfWeek, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/basic.js
new file mode 100644
index 0000000000..0cbbe195fe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/basic.js
@@ -0,0 +1,15 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.dayofyear
+description: Checking day of year for a "normal" case (non-undefined, non-boundary case, etc.)
+features: [Temporal]
+---*/
+
+const calendar = Temporal.Calendar.from("iso8601");
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar);
+assert.sameValue(datetime.dayOfYear, 323, "check day of year information");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/branding.js
new file mode 100644
index 0000000000..27970c2b80
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.dayofyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const dayOfYear = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "dayOfYear").get;
+
+assert.sameValue(typeof dayOfYear, "function");
+
+assert.throws(TypeError, () => dayOfYear.call(undefined), "undefined");
+assert.throws(TypeError, () => dayOfYear.call(null), "null");
+assert.throws(TypeError, () => dayOfYear.call(true), "true");
+assert.throws(TypeError, () => dayOfYear.call(""), "empty string");
+assert.throws(TypeError, () => dayOfYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => dayOfYear.call(1), "1");
+assert.throws(TypeError, () => dayOfYear.call({}), "plain object");
+assert.throws(TypeError, () => dayOfYear.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => dayOfYear.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..51a03fc9a3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.dayofyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dayOfYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dayOfYear");
+Object.defineProperty(Temporal.Calendar.prototype, "dayOfYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dayOfYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.dayOfYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "dayOfYear", dayOfYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/custom.js
new file mode 100644
index 0000000000..589aa29003
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.dayofyear
+description: Custom calendar tests for dayOfYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dayOfYear(...args) {
+ ++calls;
+ assert.compareArray(args, [pdt], "dayOfYear arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pdt = new Temporal.PlainDateTime(1830, 8, 25, 20, 0, 0, 0, 0, 0, calendar);
+const result = pdt.dayOfYear;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/prop-desc.js
new file mode 100644
index 0000000000..b0db9e144f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.dayofyear
+description: The "dayOfYear" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "dayOfYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/validate-calendar-value.js
new file mode 100644
index 0000000000..86dc374962
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/validate-calendar-value.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.dayofyear
+description: Validate result returned from calendar dayOfYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ dayOfYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.throws(error, () => instance.dayOfYear, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/basic.js
new file mode 100644
index 0000000000..bfa4e7afc4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/basic.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.daysinmonth
+description: Checking days in month for a "normal" case (non-undefined, non-boundary case, etc.)
+features: [Temporal]
+---*/
+
+const tests = [
+ [new Temporal.PlainDateTime(1976, 2, 18, 15, 23, 30, 123, 456, 789), 29],
+ [new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789), 30],
+ [new Temporal.PlainDateTime(1976, 12, 18, 15, 23, 30, 123, 456, 789), 31],
+ [new Temporal.PlainDateTime(1977, 2, 18, 15, 23, 30, 123, 456, 789), 28],
+];
+for (const [plainDateTime, expected] of tests) {
+ assert.sameValue(plainDateTime.daysInMonth, expected, `${expected} days in the month of ${plainDateTime}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/branding.js
new file mode 100644
index 0000000000..df0de408b3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.daysinmonth
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const daysInMonth = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "daysInMonth").get;
+
+assert.sameValue(typeof daysInMonth, "function");
+
+assert.throws(TypeError, () => daysInMonth.call(undefined), "undefined");
+assert.throws(TypeError, () => daysInMonth.call(null), "null");
+assert.throws(TypeError, () => daysInMonth.call(true), "true");
+assert.throws(TypeError, () => daysInMonth.call(""), "empty string");
+assert.throws(TypeError, () => daysInMonth.call(Symbol()), "symbol");
+assert.throws(TypeError, () => daysInMonth.call(1), "1");
+assert.throws(TypeError, () => daysInMonth.call({}), "plain object");
+assert.throws(TypeError, () => daysInMonth.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => daysInMonth.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..62e01f3615
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.daysinmonth
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const daysInMonthOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "daysInMonth");
+Object.defineProperty(Temporal.Calendar.prototype, "daysInMonth", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("daysInMonth should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.daysInMonth;
+
+Object.defineProperty(Temporal.Calendar.prototype, "daysInMonth", daysInMonthOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/custom.js
new file mode 100644
index 0000000000..b5a9807cc2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.daysinmonth
+description: Custom calendar tests for daysInMonth().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ daysInMonth(...args) {
+ ++calls;
+ assert.compareArray(args, [pdt], "daysInMonth arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pdt = new Temporal.PlainDateTime(1830, 8, 25, 20, 0, 0, 0, 0, 0, calendar);
+const result = pdt.daysInMonth;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/prop-desc.js
new file mode 100644
index 0000000000..3beeb3ffac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.daysinmonth
+description: The "daysInMonth" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "daysInMonth");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/validate-calendar-value.js
new file mode 100644
index 0000000000..2ddd63adb8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/validate-calendar-value.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.daysinmonth
+description: Validate result returned from calendar daysInMonth() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ daysInMonth() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.throws(error, () => instance.daysInMonth, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/basic.js
new file mode 100644
index 0000000000..9fa4721d75
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/basic.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.daysinweek
+description: Checking days in week for a "normal" case (non-undefined, non-boundary case, etc.)
+features: [Temporal]
+---*/
+
+const tests = [
+ new Temporal.PlainDateTime(1976, 1, 1, 15, 23, 30, 123, 456, 789),
+ new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789),
+ new Temporal.PlainDateTime(1976, 12, 31, 15, 23, 30, 123, 456, 789),
+];
+for (const plainDateTime of tests) {
+ assert.sameValue(plainDateTime.daysInWeek, 7, `Seven days in the week of ${plainDateTime}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/branding.js
new file mode 100644
index 0000000000..5983749761
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.daysinweek
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const daysInWeek = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "daysInWeek").get;
+
+assert.sameValue(typeof daysInWeek, "function");
+
+assert.throws(TypeError, () => daysInWeek.call(undefined), "undefined");
+assert.throws(TypeError, () => daysInWeek.call(null), "null");
+assert.throws(TypeError, () => daysInWeek.call(true), "true");
+assert.throws(TypeError, () => daysInWeek.call(""), "empty string");
+assert.throws(TypeError, () => daysInWeek.call(Symbol()), "symbol");
+assert.throws(TypeError, () => daysInWeek.call(1), "1");
+assert.throws(TypeError, () => daysInWeek.call({}), "plain object");
+assert.throws(TypeError, () => daysInWeek.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => daysInWeek.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..7dec28473b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.daysinweek
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const daysInWeekOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "daysInWeek");
+Object.defineProperty(Temporal.Calendar.prototype, "daysInWeek", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("daysInWeek should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.daysInWeek;
+
+Object.defineProperty(Temporal.Calendar.prototype, "daysInWeek", daysInWeekOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/custom.js
new file mode 100644
index 0000000000..ce3af6a208
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.daysinweek
+description: Custom calendar tests for daysInWeek().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ daysInWeek(...args) {
+ ++calls;
+ assert.compareArray(args, [pdt], "daysInWeek arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pdt = new Temporal.PlainDateTime(1830, 8, 25, 20, 0, 0, 0, 0, 0, calendar);
+const result = pdt.daysInWeek;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/prop-desc.js
new file mode 100644
index 0000000000..cf29852b48
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.daysinweek
+description: The "daysInWeek" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "daysInWeek");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/validate-calendar-value.js
new file mode 100644
index 0000000000..db73c9ca69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/validate-calendar-value.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.daysinweek
+description: Validate result returned from calendar daysInWeek() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ daysInWeek() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.throws(error, () => instance.daysInWeek, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/basic.js
new file mode 100644
index 0000000000..de422d729b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/basic.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.daysinyear
+description: Checking days in year for a "normal" case (non-undefined, non-boundary case, etc.)
+features: [Temporal]
+---*/
+
+assert.sameValue((new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789)).daysInYear,
+ 366, "leap year");
+assert.sameValue((new Temporal.PlainDateTime(1977, 11, 18, 15, 23, 30, 123, 456, 789)).daysInYear,
+ 365, "non-leap year");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/branding.js
new file mode 100644
index 0000000000..3a102e63d9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.daysinyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const daysInYear = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "daysInYear").get;
+
+assert.sameValue(typeof daysInYear, "function");
+
+assert.throws(TypeError, () => daysInYear.call(undefined), "undefined");
+assert.throws(TypeError, () => daysInYear.call(null), "null");
+assert.throws(TypeError, () => daysInYear.call(true), "true");
+assert.throws(TypeError, () => daysInYear.call(""), "empty string");
+assert.throws(TypeError, () => daysInYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => daysInYear.call(1), "1");
+assert.throws(TypeError, () => daysInYear.call({}), "plain object");
+assert.throws(TypeError, () => daysInYear.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => daysInYear.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..70cbdd66f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.daysinyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const daysInYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "daysInYear");
+Object.defineProperty(Temporal.Calendar.prototype, "daysInYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("daysInYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.daysInYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "daysInYear", daysInYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/custom.js
new file mode 100644
index 0000000000..ec28f93a12
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.daysinyear
+description: Custom calendar tests for daysInYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ daysInYear(...args) {
+ ++calls;
+ assert.compareArray(args, [pdt], "daysInYear arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pdt = new Temporal.PlainDateTime(1830, 8, 25, 20, 0, 0, 0, 0, 0, calendar);
+const result = pdt.daysInYear;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/prop-desc.js
new file mode 100644
index 0000000000..54c466d351
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.daysinyear
+description: The "daysInYear" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "daysInYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/validate-calendar-value.js
new file mode 100644
index 0000000000..c6fd25287b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/validate-calendar-value.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.daysinyear
+description: Validate result returned from calendar daysInYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ daysInYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.throws(error, () => instance.daysInYear, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..1afe3dc313
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const arg = { year: 2000, month: 5, day: 2, hour: 21, minute: 43, second: 5, calendar: "iso8601" };
+instance.equals(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-number.js
new file mode 100644
index 0000000000..bf97cf78d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-number.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: A number cannot be used in place of a Temporal.PlainDateTime
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.equals(arg),
+ `A number (${arg}) is not a valid ISO string for PlainDateTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-object-insufficient-data.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-object-insufficient-data.js
new file mode 100644
index 0000000000..cd663443f1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-object-insufficient-data.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: If argument is an object, it must contain sufficient information
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102);
+
+assert.throws(
+ TypeError,
+ () => dt.equals({ year: 1976 }),
+ "object must contain required properties"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-plaindate.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-plaindate.js
new file mode 100644
index 0000000000..54eb0ee4aa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-plaindate.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: Fast path for converting Temporal.PlainDate to Temporal.PlainDateTime by reading internal slots
+info: |
+ sec-temporal.plaindatetime.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.b:
+ b. If _item_ has an [[InitializedTemporalDate]] internal slot, then
+ i. Return ? CreateTemporalDateTime(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], 0, 0, 0, 0, 0, 0, _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalPlainDateTimeFastPath((date, calendar) => {
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 0, 0, 0, 0, 0, 0, calendar);
+ assert(datetime.equals(date));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..d25cf80551
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18);
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.equals(arg);
+assert.sameValue(result, true, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..7ff0a1b655
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18);
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.equals(arg);
+assert.sameValue(
+ result,
+ true,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..e532ba1fb1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-number.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.equals(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..9715239429
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-string.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18);
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.equals(arg);
+assert.sameValue(result, true, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..ba0f49ec64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.equals(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.equals(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..967839b97e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-year-zero.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..067490424a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-calendar-annotation.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[u-ca=iso8601]", "without time zone"],
+ ["1976-11-18T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["1976-11-18T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["1976-11-18T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["1976-11-18T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..f47dbb00ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..b83a73f0f0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-date-with-utc-offset.js
@@ -0,0 +1,48 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+const validStrings = [
+ "1976-11-18T15:23+00:00",
+ "1976-11-18T15:23+00:00[UTC]",
+ "1976-11-18T15:23+00:00[!UTC]",
+ "1976-11-18T15:23-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `"${arg}" is a valid UTC offset with time for PlainDateTime`
+ );
+}
+
+const invalidStrings = [
+ "2022-09-15Z",
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDateTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..cd069f5857
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-multiple-calendar.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..42a7ade474
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-multiple-time-zone.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[UTC][UTC]",
+ "1970-01-01T00:00[!UTC][UTC]",
+ "1970-01-01T00:00[UTC][!UTC]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00[UTC][foo=bar][UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-time-separators.js
new file mode 100644
index 0000000000..bef88b1f4f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-time-separators.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23", "uppercase T"],
+ ["1976-11-18t15:23", "lowercase T"],
+ ["1976-11-18 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..f1c038171e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-time-zone-annotation.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[Asia/Kolkata]", "named, with no offset"],
+ ["1976-11-18T15:23[!Europe/Vienna]", "named, with ! and no offset"],
+ ["1976-11-18T15:23[+00:00]", "numeric, with no offset"],
+ ["1976-11-18T15:23[!-02:30]", "numeric, with ! and no offset"],
+ ["1976-11-18T15:23+00:00[UTC]", "named, with offset"],
+ ["1976-11-18T15:23+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["1976-11-18T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["1976-11-18T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..1a128c2671
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-unknown-annotation.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[foo=bar]", "alone"],
+ ["1976-11-18T15:23[UTC][foo=bar]", "with time zone"],
+ ["1976-11-18T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["1976-11-18T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["1976-11-18T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..e1efc0c0ad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-with-utc-designator.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: RangeError thrown if a string with UTC designator is used as a PlainDateTime
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ "String with UTC designator should not be valid as a PlainDateTime"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-wrong-type.js
new file mode 100644
index 0000000000..25a644b4ec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-wrong-type.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDateTime
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => instance.equals(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDateTime, "Temporal.PlainDateTime, object"],
+ [Temporal.PlainDateTime.prototype, "Temporal.PlainDateTime.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.equals(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 0000000000..4e26be85b3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,44 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: Negative time fields are balanced upwards if the argument is given as ZonedDateTime
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-temporal-totemporaldatetime step 3.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ ii. 1. Return ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
+ sec-temporal.plaindatetime.prototype.until step 3:
+ 3. Set _other_ ? ToTemporalDateTime(_other_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// This code path is encountered if the time zone offset is negative and its
+// absolute value in nanoseconds is greater than the nanosecond field of the
+// exact time's epoch parts
+const tz = TemporalHelpers.specificOffsetTimeZone(-2);
+const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
+
+assert(new Temporal.PlainDateTime(1970, 1, 1, 1, 1, 1, 1, 0, 999).equals(datetime));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..73f5790fea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const instance = new Temporal.PlainDateTime(1969, 7, 24, 16, 50, 35, 0, 0, 1);
+const result = instance.equals(datetime);
+assert.sameValue(result, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..e02afefd3b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.equals(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..b2fe1b47df
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => plain.equals(zoned),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..1570b82c02
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.equals(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..06940015b9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => plain.equals(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/basic.js
new file mode 100644
index 0000000000..6788f4f879
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/basic.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: Checking a typical case (everything defined, no NaNs, nothing throws, etc.)
+features: [Temporal]
+---*/
+
+const dt1 = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+const dt2 = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102);
+const dt3 = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102);
+const dt4 = new Temporal.PlainDateTime(2019, 10, 29, 15, 23, 30, 123, 456, 789);
+const dt5 = new Temporal.PlainDateTime(1976, 11, 18, 10, 46, 38, 271, 986, 102);
+
+assert.sameValue(dt1.equals(dt1), true, "equal");
+assert.sameValue(dt1.equals(dt2), false, "unequal");
+assert.sameValue(dt2.equals(dt3), true, "equal with different objects");
+assert.sameValue(dt2.equals(dt4), false, "same date, different time");
+assert.sameValue(dt2.equals(dt5), false, "same time, different date");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/branding.js
new file mode 100644
index 0000000000..701540f5a2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const equals = Temporal.PlainDateTime.prototype.equals;
+
+assert.sameValue(typeof equals, "function");
+
+const args = [new Temporal.PlainDateTime(2022, 6, 22)];
+
+assert.throws(TypeError, () => equals.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => equals.apply(null, args), "null");
+assert.throws(TypeError, () => equals.apply(true, args), "true");
+assert.throws(TypeError, () => equals.apply("", args), "empty string");
+assert.throws(TypeError, () => equals.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => equals.apply(1, args), "1");
+assert.throws(TypeError, () => equals.apply({}, args), "plain object");
+assert.throws(TypeError, () => equals.apply(Temporal.PlainDateTime, args), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => equals.apply(Temporal.PlainDateTime.prototype, args), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..1c3f13d96d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.equals(new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321));
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/builtin.js
new file mode 100644
index 0000000000..f443c49906
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: >
+ Tests that Temporal.PlainDateTime.prototype.equals
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.equals),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.equals),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.equals),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.equals.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-checked.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-checked.js
new file mode 100644
index 0000000000..28e8d34147
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-checked.js
@@ -0,0 +1,112 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: Calendar is taken into account if the ISO data is equal
+includes: [compareArray.js,temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+
+function makeCalendar(id, objectName) {
+ const calendar = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+ };
+ TemporalHelpers.observeProperty(actual, calendar, "id", id, objectName);
+ return calendar;
+}
+
+const calendar1 = makeCalendar("A", "calendar1");
+const calendar2 = makeCalendar("A", "calendar2");
+const calendar3 = makeCalendar("B", "calendar3");
+const dt1 = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar1);
+const dt1b = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar1);
+const dt2 = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar2);
+const dt3 = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar3);
+actual.splice(0); // disregard the HasProperty checks done in the constructor
+
+assert.sameValue(dt1.equals(dt1b), true, "same calendar object");
+assert.compareArray(actual, []);
+
+assert.sameValue(dt1.equals(dt2), true, "same calendar string");
+assert.compareArray(actual, ["get calendar1.id", "get calendar2.id"]);
+
+actual.splice(0); // empty it for the next check
+assert.sameValue(dt1.equals(dt3), false, "different calendar string");
+assert.compareArray(actual, ["get calendar1.id", "get calendar3.id"]);
+
+const calendar4 = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ get id() { TemporalHelpers.assertUnreachable('should not get calendar4.id'); },
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const calendar5 = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ get id() { TemporalHelpers.assertUnreachable('should not get calendar5.id'); },
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const dt4 = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar4);
+const dt5 = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102, calendar4);
+const dt6 = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102, calendar5);
+assert.sameValue(dt4.equals(dt5), false, "not equal same calendar");
+assert.sameValue(dt4.equals(dt6), false, "not equal different calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..a6af1e4f24
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-datefromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.equals(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-fields-iterable.js
new file mode 100644
index 0000000000..c8c128aed3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-fields-iterable.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindatetime.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+datetime.equals({ year: 2005, month: 6, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-temporal-object.js
new file mode 100644
index 0000000000..3b42636d2d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindatetime.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, temporalObject);
+ datetime.equals({ year: 2005, month: 6, day: 2, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/cast.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/cast.js
new file mode 100644
index 0000000000..5ff5d4baf1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/cast.js
@@ -0,0 +1,49 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: Argument may be cast
+features: [Temporal]
+---*/
+
+const dt1 = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+const dt2 = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102);
+
+assert.sameValue(
+ dt1.equals({
+ year: 1976,
+ month: 11,
+ day: 18,
+ hour: 15,
+ minute: 23,
+ second: 30,
+ millisecond: 123,
+ microsecond: 456,
+ nanosecond: 789
+ }),
+ true,
+ "casts argument (plain object, positive)"
+);
+
+
+assert.sameValue(
+ dt2.equals({ year: 1976, month: 11, day: 18, hour: 15 }),
+ false,
+ "casts argument (plain object, negative)"
+);
+
+assert.sameValue(
+ dt1.equals("1976-11-18T15:23:30.123456789"),
+ true,
+ "casts argument (string, positive)"
+);
+
+assert.sameValue(
+ dt2.equals("1976-11-18T15:23:30.123456789"),
+ false,
+ "casts argument (string, negative)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..3a444f6002
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/constructor-in-calendar-fields.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+assert.throws(RangeError, () => instance.equals(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..f8900be8ce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['hour'], ['microsecond'], ['millisecond'], ['minute'], ['month'], ['monthCode'], ['nanosecond'], ['second'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+ assert.throws(RangeError, () => instance.equals(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..ba5cde17c8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/infinity-throws-rangeerror.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindatetime.prototype.equals
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15);
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.equals({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.equals({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/leap-second.js
new file mode 100644
index 0000000000..523ab77e2f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/leap-second.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: Leap second is a valid ISO string for PlainDateTime
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2016, 12, 31, 23, 59, 59);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.equals(arg);
+assert.sameValue(
+ result1,
+ true,
+ "leap second is a valid ISO string for PlainDateTime"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.equals(arg);
+assert.sameValue(
+ result2,
+ true,
+ "second: 60 is ignored in property bag for PlainDateTime"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/length.js
new file mode 100644
index 0000000000..496ec4fe63
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: Temporal.PlainDateTime.prototype.equals.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.equals, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/name.js
new file mode 100644
index 0000000000..4903a11e40
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: Temporal.PlainDateTime.prototype.equals.name is "equals".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.equals, "name", {
+ value: "equals",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/not-a-constructor.js
new file mode 100644
index 0000000000..ab15972cee
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: >
+ Temporal.PlainDateTime.prototype.equals does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.equals();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.equals), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.equals)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/prop-desc.js
new file mode 100644
index 0000000000..f7be4f549c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: The "equals" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.equals,
+ "function",
+ "`typeof PlainDateTime.prototype.equals` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "equals", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..fd3064caed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/proto-in-calendar-fields.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+assert.throws(RangeError, () => instance.equals(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/read-time-fields-before-datefromfields.js
new file mode 100644
index 0000000000..cd9066eb8f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/read-time-fields-before-datefromfields.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.plaindatetime.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.e:
+ e. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const datetime = new Temporal.PlainDateTime(2021, 3, 31, 12, 34, 56, 987, 654, 321, calendar);
+const result = datetime.equals({ year: 2021, month: 3, day: 31, hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321, calendar });
+
+assert(result, "time fields are not modified");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/year-zero.js
new file mode 100644
index 0000000000..d8fe8e7b82
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/year-zero.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-12-07",
+ "-000000-12-07T03:24:30",
+ "-000000-12-07T03:24:30+01:00",
+ "-000000-12-07T03:24:30+00:00[UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/branding.js
new file mode 100644
index 0000000000..b0a9322aac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.getcalendar
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const getCalendar = Temporal.PlainDateTime.prototype.getCalendar;
+
+assert.sameValue(typeof getCalendar, "function");
+
+assert.throws(TypeError, () => getCalendar.call(undefined), "undefined");
+assert.throws(TypeError, () => getCalendar.call(null), "null");
+assert.throws(TypeError, () => getCalendar.call(true), "true");
+assert.throws(TypeError, () => getCalendar.call(""), "empty string");
+assert.throws(TypeError, () => getCalendar.call(Symbol()), "symbol");
+assert.throws(TypeError, () => getCalendar.call(1), "1");
+assert.throws(TypeError, () => getCalendar.call({}), "plain object");
+assert.throws(TypeError, () => getCalendar.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => getCalendar.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/builtin.js
new file mode 100644
index 0000000000..593d4112f1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.getcalendar
+description: >
+ Tests that Temporal.PlainDateTime.prototype.getCalendar
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.getCalendar),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.getCalendar),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.getCalendar),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.getCalendar.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/length.js
new file mode 100644
index 0000000000..bf0fd6d852
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.getcalendar
+description: Temporal.PlainDateTime.prototype.getCalendar.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.getCalendar, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/name.js
new file mode 100644
index 0000000000..c9ec696b05
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.getcalendar
+description: Temporal.PlainDateTime.prototype.getCalendar.name is "getCalendar".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.getCalendar, "name", {
+ value: "getCalendar",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/not-a-constructor.js
new file mode 100644
index 0000000000..8e35262749
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.getcalendar
+description: >
+ Temporal.PlainDateTime.prototype.getCalendar does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.getCalendar();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.getCalendar), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.getCalendar)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/prop-desc.js
new file mode 100644
index 0000000000..cdfecc5979
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.getcalendar
+description: The "getCalendar" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.getCalendar,
+ "function",
+ "`typeof PlainDateTime.prototype.getCalendar` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "getCalendar", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/branding.js
new file mode 100644
index 0000000000..a1c1a6d9e3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.getisofields
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const getISOFields = Temporal.PlainDateTime.prototype.getISOFields;
+
+assert.sameValue(typeof getISOFields, "function");
+
+assert.throws(TypeError, () => getISOFields.call(undefined), "undefined");
+assert.throws(TypeError, () => getISOFields.call(null), "null");
+assert.throws(TypeError, () => getISOFields.call(true), "true");
+assert.throws(TypeError, () => getISOFields.call(""), "empty string");
+assert.throws(TypeError, () => getISOFields.call(Symbol()), "symbol");
+assert.throws(TypeError, () => getISOFields.call(1), "1");
+assert.throws(TypeError, () => getISOFields.call({}), "plain object");
+assert.throws(TypeError, () => getISOFields.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => getISOFields.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/builtin.js
new file mode 100644
index 0000000000..c6dc7cb134
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.getisofields
+description: >
+ Tests that Temporal.PlainDateTime.prototype.getISOFields
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.getISOFields),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.getISOFields),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.getISOFields),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.getISOFields.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/custom.js
new file mode 100644
index 0000000000..dda36e26f9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/custom.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.getisofields
+description: getISOFields does not call into user code.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarThrowEverything();
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+const result = instance.getISOFields();
+
+assert.sameValue(result.isoYear, 2000, "isoYear result");
+assert.sameValue(result.isoMonth, 5, "isoMonth result");
+assert.sameValue(result.isoDay, 2, "isoDay result");
+assert.sameValue(result.isoHour, 12, "isoHour result");
+assert.sameValue(result.isoMinute, 34, "isoMinute result");
+assert.sameValue(result.isoSecond, 56, "isoSecond result");
+assert.sameValue(result.isoMillisecond, 987, "isoMillisecond result");
+assert.sameValue(result.isoMicrosecond, 654, "isoMicrosecond result");
+assert.sameValue(result.isoNanosecond, 321, "isoNanosecond result");
+assert.sameValue(result.calendar, calendar, "calendar result");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-names.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-names.js
new file mode 100644
index 0000000000..7e0af96024
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-names.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.getisofields
+description: Correct field names on the object returned from getISOFields
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const result = datetime.getISOFields();
+assert.sameValue(result.isoYear, 2000, "isoYear result");
+assert.sameValue(result.isoMonth, 5, "isoMonth result");
+assert.sameValue(result.isoDay, 2, "isoDay result");
+assert.sameValue(result.isoHour, 12, "isoHour result");
+assert.sameValue(result.isoMinute, 34, "isoMinute result");
+assert.sameValue(result.isoSecond, 56, "isoSecond result");
+assert.sameValue(result.isoMillisecond, 987, "isoMillisecond result");
+assert.sameValue(result.isoMicrosecond, 654, "isoMicrosecond result");
+assert.sameValue(result.isoNanosecond, 321, "isoNanosecond result");
+assert.sameValue(result.calendar, "iso8601", "calendar result");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-prop-desc.js
new file mode 100644
index 0000000000..41e6b2d1e6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-prop-desc.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.getisofields
+description: Properties on the returned object have the correct descriptor
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "calendar",
+ "isoDay",
+ "isoHour",
+ "isoMicrosecond",
+ "isoMillisecond",
+ "isoMinute",
+ "isoMonth",
+ "isoNanosecond",
+ "isoSecond",
+ "isoYear",
+];
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const result = datetime.getISOFields();
+
+for (const property of expected) {
+ verifyProperty(result, property, {
+ writable: true,
+ enumerable: true,
+ configurable: true,
+ });
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-traversal-order.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-traversal-order.js
new file mode 100644
index 0000000000..6d1f676264
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-traversal-order.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.getisofields
+description: Properties added in correct order to object returned from getISOFields
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "calendar",
+ "isoDay",
+ "isoHour",
+ "isoMicrosecond",
+ "isoMillisecond",
+ "isoMinute",
+ "isoMonth",
+ "isoNanosecond",
+ "isoSecond",
+ "isoYear",
+];
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const result = datetime.getISOFields();
+
+assert.compareArray(Object.keys(result), expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/length.js
new file mode 100644
index 0000000000..3580c33e00
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.getisofields
+description: Temporal.PlainDateTime.prototype.getISOFields.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.getISOFields, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/name.js
new file mode 100644
index 0000000000..2f6a0b0691
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.getisofields
+description: Temporal.PlainDateTime.prototype.getISOFields.name is "getISOFields".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.getISOFields, "name", {
+ value: "getISOFields",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/not-a-constructor.js
new file mode 100644
index 0000000000..745bfb8a9a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.getisofields
+description: >
+ Temporal.PlainDateTime.prototype.getISOFields does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.getISOFields();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.getISOFields), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.getISOFields)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/prop-desc.js
new file mode 100644
index 0000000000..8addd7338d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.getisofields
+description: The "getISOFields" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.getISOFields,
+ "function",
+ "`typeof PlainDateTime.prototype.getISOFields` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "getISOFields", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/prototype.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/prototype.js
new file mode 100644
index 0000000000..9b2fe2de33
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/prototype.js
@@ -0,0 +1,15 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.getisofields
+description: Correct prototype on the object returned from getISOFields
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const result = instance.getISOFields();
+assert.sameValue(Object.getPrototypeOf(result), Object.prototype, "prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/branding.js
new file mode 100644
index 0000000000..7f25864f22
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.hour
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const hour = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "hour").get;
+
+assert.sameValue(typeof hour, "function");
+
+assert.throws(TypeError, () => hour.call(undefined), "undefined");
+assert.throws(TypeError, () => hour.call(null), "null");
+assert.throws(TypeError, () => hour.call(true), "true");
+assert.throws(TypeError, () => hour.call(""), "empty string");
+assert.throws(TypeError, () => hour.call(Symbol()), "symbol");
+assert.throws(TypeError, () => hour.call(1), "1");
+assert.throws(TypeError, () => hour.call({}), "plain object");
+assert.throws(TypeError, () => hour.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => hour.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/prop-desc.js
new file mode 100644
index 0000000000..be1f170bea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.hour
+description: The "hour" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "hour");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/basic.js
new file mode 100644
index 0000000000..a6b88825c3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/basic.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.inleapyear
+description: Basic test for inLeapYear
+features: [Temporal]
+---*/
+
+assert.sameValue((new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789)).inLeapYear,
+ true, "leap year");
+assert.sameValue((new Temporal.PlainDateTime(1977, 11, 18, 15, 23, 30, 123, 456, 789)).inLeapYear,
+ false, "non-leap year");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/branding.js
new file mode 100644
index 0000000000..476c99fa05
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.inleapyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const inLeapYear = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "inLeapYear").get;
+
+assert.sameValue(typeof inLeapYear, "function");
+
+assert.throws(TypeError, () => inLeapYear.call(undefined), "undefined");
+assert.throws(TypeError, () => inLeapYear.call(null), "null");
+assert.throws(TypeError, () => inLeapYear.call(true), "true");
+assert.throws(TypeError, () => inLeapYear.call(""), "empty string");
+assert.throws(TypeError, () => inLeapYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => inLeapYear.call(1), "1");
+assert.throws(TypeError, () => inLeapYear.call({}), "plain object");
+assert.throws(TypeError, () => inLeapYear.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => inLeapYear.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..122539fc57
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.inleapyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const inLeapYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "inLeapYear");
+Object.defineProperty(Temporal.Calendar.prototype, "inLeapYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("inLeapYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.inLeapYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "inLeapYear", inLeapYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/custom.js
new file mode 100644
index 0000000000..25daeb1c2b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.inleapyear
+description: Custom calendar tests for inLeapYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ inLeapYear(...args) {
+ ++calls;
+ assert.compareArray(args, [pdt], "inLeapYear arguments");
+ return true;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pdt = new Temporal.PlainDateTime(1830, 8, 25, 20, 0, 0, 0, 0, 0, calendar);
+const result = pdt.inLeapYear;
+assert.sameValue(result, true, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/prop-desc.js
new file mode 100644
index 0000000000..30bdbbeaed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.inleapyear
+description: The "inLeapYear" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "inLeapYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/validate-calendar-value.js
new file mode 100644
index 0000000000..bbcde3216a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/validate-calendar-value.js
@@ -0,0 +1,56 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.inleapyear
+description: Validate result returned from calendar inLeapYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [0, TypeError],
+ [-0, TypeError],
+ [42, TypeError],
+ [7.1, TypeError],
+ [NaN, TypeError],
+ [Infinity, TypeError],
+ [-Infinity, TypeError],
+ ["", TypeError],
+ ["a string", TypeError],
+ ["0", TypeError],
+ [Symbol("foo"), TypeError],
+ [0n, TypeError],
+ [42n, TypeError],
+ [{}, TypeError],
+ [{valueOf() { return false; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ inLeapYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.throws(error, () => instance.inLeapYear, `${typeof result} ${String(result)} not converted to boolean`);
+});
+
+const preservedResults = [
+ true,
+ false,
+];
+
+preservedResults.forEach(result => {
+ const calendar = new class extends Temporal.Calendar {
+ inLeapYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.sameValue(instance.inLeapYear, result, `${typeof result} ${String(result)} preserved`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/branding.js
new file mode 100644
index 0000000000..ae0dfd4306
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.microsecond
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const microsecond = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "microsecond").get;
+
+assert.sameValue(typeof microsecond, "function");
+
+assert.throws(TypeError, () => microsecond.call(undefined), "undefined");
+assert.throws(TypeError, () => microsecond.call(null), "null");
+assert.throws(TypeError, () => microsecond.call(true), "true");
+assert.throws(TypeError, () => microsecond.call(""), "empty string");
+assert.throws(TypeError, () => microsecond.call(Symbol()), "symbol");
+assert.throws(TypeError, () => microsecond.call(1), "1");
+assert.throws(TypeError, () => microsecond.call({}), "plain object");
+assert.throws(TypeError, () => microsecond.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => microsecond.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/prop-desc.js
new file mode 100644
index 0000000000..f14748090e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.microsecond
+description: The "microsecond" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "microsecond");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/branding.js
new file mode 100644
index 0000000000..806197f618
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.millisecond
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const millisecond = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "millisecond").get;
+
+assert.sameValue(typeof millisecond, "function");
+
+assert.throws(TypeError, () => millisecond.call(undefined), "undefined");
+assert.throws(TypeError, () => millisecond.call(null), "null");
+assert.throws(TypeError, () => millisecond.call(true), "true");
+assert.throws(TypeError, () => millisecond.call(""), "empty string");
+assert.throws(TypeError, () => millisecond.call(Symbol()), "symbol");
+assert.throws(TypeError, () => millisecond.call(1), "1");
+assert.throws(TypeError, () => millisecond.call({}), "plain object");
+assert.throws(TypeError, () => millisecond.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => millisecond.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/prop-desc.js
new file mode 100644
index 0000000000..67667b89f2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.millisecond
+description: The "millisecond" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "millisecond");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/branding.js
new file mode 100644
index 0000000000..b8c7df0cbc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.minute
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const minute = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "minute").get;
+
+assert.sameValue(typeof minute, "function");
+
+assert.throws(TypeError, () => minute.call(undefined), "undefined");
+assert.throws(TypeError, () => minute.call(null), "null");
+assert.throws(TypeError, () => minute.call(true), "true");
+assert.throws(TypeError, () => minute.call(""), "empty string");
+assert.throws(TypeError, () => minute.call(Symbol()), "symbol");
+assert.throws(TypeError, () => minute.call(1), "1");
+assert.throws(TypeError, () => minute.call({}), "plain object");
+assert.throws(TypeError, () => minute.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => minute.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/prop-desc.js
new file mode 100644
index 0000000000..b8294ac693
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.minute
+description: The "minute" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "minute");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/branding.js
new file mode 100644
index 0000000000..0b3cfafaac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.month
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const month = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "month").get;
+
+assert.sameValue(typeof month, "function");
+
+assert.throws(TypeError, () => month.call(undefined), "undefined");
+assert.throws(TypeError, () => month.call(null), "null");
+assert.throws(TypeError, () => month.call(true), "true");
+assert.throws(TypeError, () => month.call(""), "empty string");
+assert.throws(TypeError, () => month.call(Symbol()), "symbol");
+assert.throws(TypeError, () => month.call(1), "1");
+assert.throws(TypeError, () => month.call({}), "plain object");
+assert.throws(TypeError, () => month.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => month.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..297016272f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.month
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const monthOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "month");
+Object.defineProperty(Temporal.Calendar.prototype, "month", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("month should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.month;
+
+Object.defineProperty(Temporal.Calendar.prototype, "month", monthOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/custom.js
new file mode 100644
index 0000000000..2bc1a17f12
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.month
+description: Custom calendar tests for month().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ month(...args) {
+ ++calls;
+ assert.compareArray(args, [pdt], "month arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pdt = new Temporal.PlainDateTime(1830, 8, 25, 20, 0, 0, 0, 0, 0, calendar);
+const result = pdt.month;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/prop-desc.js
new file mode 100644
index 0000000000..513395b28a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.month
+description: The "month" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "month");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/validate-calendar-value.js
new file mode 100644
index 0000000000..d53f50541d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/validate-calendar-value.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.month
+description: Validate result returned from calendar month() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ month() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.throws(error, () => instance.month, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/branding.js
new file mode 100644
index 0000000000..a327685c07
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.monthcode
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const monthCode = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "monthCode").get;
+
+assert.sameValue(typeof monthCode, "function");
+
+assert.throws(TypeError, () => monthCode.call(undefined), "undefined");
+assert.throws(TypeError, () => monthCode.call(null), "null");
+assert.throws(TypeError, () => monthCode.call(true), "true");
+assert.throws(TypeError, () => monthCode.call(""), "empty string");
+assert.throws(TypeError, () => monthCode.call(Symbol()), "symbol");
+assert.throws(TypeError, () => monthCode.call(1), "1");
+assert.throws(TypeError, () => monthCode.call({}), "plain object");
+assert.throws(TypeError, () => monthCode.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => monthCode.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..eec179a36a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.monthcode
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const monthCodeOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "monthCode");
+Object.defineProperty(Temporal.Calendar.prototype, "monthCode", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("monthCode should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.monthCode;
+
+Object.defineProperty(Temporal.Calendar.prototype, "monthCode", monthCodeOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/custom.js
new file mode 100644
index 0000000000..27ebb31985
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.monthcode
+description: Custom calendar tests for monthCode().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ monthCode(...args) {
+ ++calls;
+ assert.compareArray(args, [pdt], "monthCode arguments");
+ return "M01";
+ }
+}
+
+const calendar = new CustomCalendar();
+const pdt = new Temporal.PlainDateTime(1830, 8, 25, 20, 0, 0, 0, 0, 0, calendar);
+const result = pdt.monthCode;
+assert.sameValue(result, "M01", "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/prop-desc.js
new file mode 100644
index 0000000000..0f8fb871ce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.monthcode
+description: The "monthCode" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "monthCode");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/validate-calendar-value.js
new file mode 100644
index 0000000000..1f17391289
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/validate-calendar-value.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.monthcode
+description: Validate result returned from calendar monthCode() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [Symbol("foo"), TypeError],
+ [null, TypeError],
+ [true, TypeError],
+ [false, TypeError],
+ [7.1, TypeError],
+ [{toString() { return "M01"; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ monthCode() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.throws(error, () => instance.monthCode, `${typeof result} ${String(result)} not converted to string`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/basic.js
new file mode 100644
index 0000000000..a1e7d322ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/basic.js
@@ -0,0 +1,15 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.monthsinyear
+description: Checking months in year for a "normal" case (non-undefined, non-boundary case, etc.)
+features: [Temporal]
+---*/
+
+const calendar = Temporal.Calendar.from("iso8601");
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar);
+assert.sameValue(datetime.monthsInYear, 12, "check months in year information");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/branding.js
new file mode 100644
index 0000000000..24380ac91f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.monthsinyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const monthsInYear = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "monthsInYear").get;
+
+assert.sameValue(typeof monthsInYear, "function");
+
+assert.throws(TypeError, () => monthsInYear.call(undefined), "undefined");
+assert.throws(TypeError, () => monthsInYear.call(null), "null");
+assert.throws(TypeError, () => monthsInYear.call(true), "true");
+assert.throws(TypeError, () => monthsInYear.call(""), "empty string");
+assert.throws(TypeError, () => monthsInYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => monthsInYear.call(1), "1");
+assert.throws(TypeError, () => monthsInYear.call({}), "plain object");
+assert.throws(TypeError, () => monthsInYear.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => monthsInYear.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..b9a114f28c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.monthsinyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const monthsInYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "monthsInYear");
+Object.defineProperty(Temporal.Calendar.prototype, "monthsInYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("monthsInYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.monthsInYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "monthsInYear", monthsInYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/custom.js
new file mode 100644
index 0000000000..c0af9d53c8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.monthsinyear
+description: Custom calendar tests for monthsInYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ monthsInYear(...args) {
+ ++calls;
+ assert.compareArray(args, [pdt], "monthsInYear arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pdt = new Temporal.PlainDateTime(1830, 8, 25, 20, 0, 0, 0, 0, 0, calendar);
+const result = pdt.monthsInYear;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/prop-desc.js
new file mode 100644
index 0000000000..17d83e9ddd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.monthsinyear
+description: The "monthsInYear" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "monthsInYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/validate-calendar-value.js
new file mode 100644
index 0000000000..0bdce32ca5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/validate-calendar-value.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.monthsinyear
+description: Validate result returned from calendar monthsInYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ monthsInYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.throws(error, () => instance.monthsInYear, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/branding.js
new file mode 100644
index 0000000000..45a90d8547
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.nanosecond
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const nanosecond = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "nanosecond").get;
+
+assert.sameValue(typeof nanosecond, "function");
+
+assert.throws(TypeError, () => nanosecond.call(undefined), "undefined");
+assert.throws(TypeError, () => nanosecond.call(null), "null");
+assert.throws(TypeError, () => nanosecond.call(true), "true");
+assert.throws(TypeError, () => nanosecond.call(""), "empty string");
+assert.throws(TypeError, () => nanosecond.call(Symbol()), "symbol");
+assert.throws(TypeError, () => nanosecond.call(1), "1");
+assert.throws(TypeError, () => nanosecond.call({}), "plain object");
+assert.throws(TypeError, () => nanosecond.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => nanosecond.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/prop-desc.js
new file mode 100644
index 0000000000..89b1843e50
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.nanosecond
+description: The "nanosecond" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "nanosecond");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/prop-desc.js
new file mode 100644
index 0000000000..c95a815a3c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/prop-desc.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-plaindatetime-prototype
+description: The "prototype" property of Temporal.PlainDateTime
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(typeof Temporal.PlainDateTime.prototype, "object");
+assert.notSameValue(Temporal.PlainDateTime.prototype, null);
+
+verifyProperty(Temporal.PlainDateTime, "prototype", {
+ writable: false,
+ enumerable: false,
+ configurable: false,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/balance.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/balance.js
new file mode 100644
index 0000000000..550f5ccd88
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/balance.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Rounding balances to the next smallest unit
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 23, 59, 59, 999, 999, 999);
+
+["day", "hour", "minute", "second", "millisecond", "microsecond"].forEach((smallestUnit) => {
+ TemporalHelpers.assertPlainDateTime(
+ dt.round({ smallestUnit }),
+ 1976, 11, "M11", 19, 0, 0, 0, 0, 0, 0,
+ `balances to next ${smallestUnit}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/branding.js
new file mode 100644
index 0000000000..e8f620dbc2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const round = Temporal.PlainDateTime.prototype.round;
+
+assert.sameValue(typeof round, "function");
+
+const args = ['hour'];
+
+assert.throws(TypeError, () => round.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => round.apply(null, args), "null");
+assert.throws(TypeError, () => round.apply(true, args), "true");
+assert.throws(TypeError, () => round.apply("", args), "empty string");
+assert.throws(TypeError, () => round.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => round.apply(1, args), "1");
+assert.throws(TypeError, () => round.apply({}, args), "plain object");
+assert.throws(TypeError, () => round.apply(Temporal.PlainDateTime, args), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => round.apply(Temporal.PlainDateTime.prototype, args), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/builtin.js
new file mode 100644
index 0000000000..f906a20fa6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: >
+ Tests that Temporal.PlainDateTime.prototype.round
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.round),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.round),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.round),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.round.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/length.js
new file mode 100644
index 0000000000..2f32b93ea5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Temporal.PlainDateTime.prototype.round.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.round, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/limits.js
new file mode 100644
index 0000000000..7a12038973
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/limits.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Checking limits of representable PlainDateTime
+features: [Temporal]
+---*/
+
+const min = new Temporal.PlainDateTime(-271821, 4, 19, 0, 0, 0, 0, 0, 1);
+const max = new Temporal.PlainDateTime(275760, 9, 13, 23, 59, 59, 999, 999, 999);
+
+["day", "hour", "minute", "second", "millisecond", "microsecond"].forEach((smallestUnit) => {
+ assert.throws(
+ RangeError,
+ () => min.round({ smallestUnit, roundingMode: "floor" }),
+ `rounding beyond limit (unit = ${smallestUnit}, rounding mode = floor)`
+ );
+ assert.throws(
+ RangeError,
+ () => max.round({ smallestUnit, roundingMode: "ceil" }),
+ `rounding beyond limit (unit = ${smallestUnit}, rounding mode = ceil)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/name.js
new file mode 100644
index 0000000000..bd1fc4cb17
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Temporal.PlainDateTime.prototype.round.name is "round".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.round, "name", {
+ value: "round",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/not-a-constructor.js
new file mode 100644
index 0000000000..664de7a906
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: >
+ Temporal.PlainDateTime.prototype.round does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.round();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.round), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.round)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/options-wrong-type.js
new file mode 100644
index 0000000000..b30b8481a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/options-wrong-type.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: TypeError thrown when options argument is missing or a non-string primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ undefined,
+ null,
+ true,
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+assert.throws(TypeError, () => instance.round(), "TypeError on missing options argument");
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.round(value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/prop-desc.js
new file mode 100644
index 0000000000..f4dee3b7c9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: The "round" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.round,
+ "function",
+ "`typeof PlainDateTime.prototype.round` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "round", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/rounding-direction.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/rounding-direction.js
new file mode 100644
index 0000000000..2193d200b2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/rounding-direction.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Rounding down is towards the Big Bang, not the epoch or 1 BCE
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(-99, 12, 15, 12, 0, 0, 500);
+TemporalHelpers.assertPlainDateTime(
+ instance.round({ smallestUnit: "second", roundingMode: "floor" }),
+ -99, 12, "M12", 15, 12, 0, 0, 0, 0, 0,
+ "Rounding down is towards the Big Bang, not the epoch or 1 BCE (roundingMode floor)"
+);
+TemporalHelpers.assertPlainDateTime(
+ instance.round({ smallestUnit: "second", roundingMode: "trunc" }),
+ -99, 12, "M12", 15, 12, 0, 0, 0, 0, 0,
+ "Rounding down is towards the Big Bang, not the epoch or 1 BCE (roundingMode trunc)"
+);
+TemporalHelpers.assertPlainDateTime(
+ instance.round({ smallestUnit: "second", roundingMode: "ceil" }),
+ -99, 12, "M12", 15, 12, 0, 1, 0, 0, 0,
+ "Rounding up is away from the Big Bang, not the epoch or 1 BCE (roundingMode ceil)"
+);
+TemporalHelpers.assertPlainDateTime(
+ instance.round({ smallestUnit: "second", roundingMode: "halfExpand" }),
+ -99, 12, "M12", 15, 12, 0, 1, 0, 0, 0,
+ "Rounding up is away from the Big Bang, not the epoch or 1 BCE (roundingMode halfExpand)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-divides.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-divides.js
new file mode 100644
index 0000000000..51bdf65d7f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-divides.js
@@ -0,0 +1,56 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Rounding increment should properly divide the relevant time unit
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 456, 789);
+
+[1, 2, 3, 4, 6, 8, 12].forEach((roundingIncrement) => {
+ assert.sameValue(
+ dt.round({ smallestUnit: "hour", roundingIncrement }) instanceof Temporal.PlainDateTime,
+ true,
+ `valid hour increments divide into 24 (rounding increment = ${roundingIncrement})`);
+});
+
+["minute", "second"].forEach((smallestUnit) => {
+ [1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30].forEach((roundingIncrement) => {
+ assert.sameValue(
+ dt.round({ smallestUnit, roundingIncrement }) instanceof Temporal.PlainDateTime,
+ true,
+ `valid ${smallestUnit} increments divide into 60 (rounding increment = ${roundingIncrement})`
+ );
+ });
+});
+
+["millisecond", "microsecond", "nanosecond"].forEach((smallestUnit) => {
+ [1, 2, 4, 5, 8, 10, 20, 25, 40, 50, 100, 125, 200, 250, 500].forEach((roundingIncrement) => {
+ assert.sameValue(
+ dt.round({ smallestUnit, roundingIncrement }) instanceof Temporal.PlainDateTime,
+ true,
+ `valid ${smallestUnit} increments divide into 1000 (rounding increment = ${roundingIncrement})`);
+ });
+});
+
+const nextIncrements = {
+ "hour": 24,
+ "minute": 60,
+ "second": 60,
+ "millisecond": 1000,
+ "microsecond": 1000,
+ "nanosecond": 1000
+};
+
+Object.entries(nextIncrements).forEach(([unit, next]) => {
+ assert.throws(
+ RangeError,
+ () => dt.round({ smallestUnit: unit, roundingIncrement: next }),
+ `throws on increments that are equal to the next highest (unit = ${unit}, increment = ${next})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-does-not-divide.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-does-not-divide.js
new file mode 100644
index 0000000000..df4a40dfe6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-does-not-divide.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Throw exception if the rounding unit does not properly divide the relevant time unit
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 456, 789);
+const units = ["day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"];
+units.forEach((unit) => {
+ assert.throws(
+ RangeError,
+ () => dt.round({ smallestUnit: unit, roundingIncrement: 29 }),
+ `throws on increments that do not divide evenly into the next highest (unit = ${unit})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-nan.js
new file mode 100644
index 0000000000..c87e6f24c3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-nan.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: RangeError thrown when roundingIncrement option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal-totemporaldatetimeroundingincrement step 5:
+ 5. Return ? ToTemporalRoundingIncrement(_normalizedOptions_, _maximum_, *false*).
+ sec-temporal.plaindatetime.prototype.round step 8:
+ 8. Let _roundingIncrement_ be ? ToTemporalDateTimeRoundingIncrement(_options_, _smallestUnit_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+assert.throws(RangeError, () => datetime.round({ smallestUnit: 'second', roundingIncrement: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-non-integer.js
new file mode 100644
index 0000000000..646f4e2143
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-non-integer.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Rounding for roundingIncrement option
+info: |
+ ToTemporalRoundingIncrement ( _normalizedOptions_ )
+
+ 1. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, *"number"*, *undefined*, *1*<sub>𝔽</sub>).
+ 2. If _increment_ is not finite, throw a *RangeError* exception.
+ 3. Let _integerIncrement_ be truncate(ℝ(_increment_)).
+ 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception.
+ 5. Return _integerIncrement_.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 5);
+const result = datetime.round({ smallestUnit: "nanosecond", roundingIncrement: 2.5, roundingMode: "expand" });
+TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 56, 0, 0, 6, "roundingIncrement 2.5 truncates to 2");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-one-day.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-one-day.js
new file mode 100644
index 0000000000..7d48e74944
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-one-day.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: One day is a valid rounding increment
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 456, 789);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.round({ smallestUnit: "day", roundingIncrement: 1 }),
+ 1976, 11, "M11", 19, 0, 0, 0, 0, 0, 0,
+ "1 day is a valid increment"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-out-of-range.js
new file mode 100644
index 0000000000..4c3be90a69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-out-of-range.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: RangeError thrown when roundingIncrement option out of range
+info: |
+ ToTemporalRoundingIncrement ( _normalizedOptions_ )
+
+ 1. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, *"number"*, *undefined*, *1*<sub>𝔽</sub>).
+ 2. If _increment_ is not finite, throw a *RangeError* exception.
+ 3. Let _integerIncrement_ be truncate(ℝ(_increment_)).
+ 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception.
+ 5. Return _integerIncrement_.
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 5);
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: -1 }));
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: 0 }));
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: 0.9 }));
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: 1e9 + 1 }));
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: Infinity }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-undefined.js
new file mode 100644
index 0000000000..d5bbcce128
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-undefined.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Fallback value for roundingIncrement option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal-totemporaldatetimeroundingincrement step 5:
+ 5. Return ? ToTemporalRoundingIncrement(_normalizedOptions_, _maximum_, *false*).
+ sec-temporal.plaindatetime.prototype.round step 8:
+ 8. Let _roundingIncrement_ be ? ToTemporalDateTimeRoundingIncrement(_options_, _smallestUnit_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const explicit = datetime.round({ smallestUnit: 'second', roundingIncrement: undefined });
+TemporalHelpers.assertPlainDateTime(explicit, 2000, 5, "M05", 2, 12, 34, 57, 0, 0, 0, "default roundingIncrement is 1");
+
+const implicit = datetime.round({ smallestUnit: 'second' });
+TemporalHelpers.assertPlainDateTime(implicit, 2000, 5, "M05", 2, 12, 34, 57, 0, 0, 0, "default roundingIncrement is 1");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-wrong-type.js
new file mode 100644
index 0000000000..05c5d22697
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-wrong-type.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Type conversions for roundingIncrement option
+info: |
+ sec-getoption step 8.a:
+ a. Set _value_ to ? ToNumber(value).
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal-totemporaldatetimeroundingincrement step 5:
+ 5. Return ? ToTemporalRoundingIncrement(_normalizedOptions_, _maximum_, *false*).
+ sec-temporal.plaindatetime.prototype.round step 8:
+ 8. Let _roundingIncrement_ be ? ToTemporalDateTimeRoundingIncrement(_options_, _smallestUnit_).
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => datetime.round({ smallestUnit: 'second', roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 57, 0, 0, 0, descr),
+ (result, descr) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 56, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-basic.js
new file mode 100644
index 0000000000..085b8bac3b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-basic.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Basic checks for rounding mode
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 456, 789);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.round({ smallestUnit: "hour", roundingIncrement: 4 }),
+ 1976, 11, "M11", 18, 16, 0, 0, 0, 0, 0,
+ "rounds to an increment of hours"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.round({ smallestUnit: "minute", roundingIncrement: 15 }),
+ 1976, 11, "M11", 18, 14, 30, 0, 0, 0, 0,
+ "rounds to an increment of minutes"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.round({ smallestUnit: "second", roundingIncrement: 30 }),
+ 1976, 11, "M11", 18, 14, 23, 30, 0, 0, 0,
+ "rounds to an increment of seconds"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.round({ smallestUnit: "millisecond", roundingIncrement: 10 }),
+ 1976, 11, "M11", 18, 14, 23, 30, 120, 0, 0,
+ "rounds to an increment of milliseconds"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.round({ smallestUnit: "microsecond", roundingIncrement: 10 }),
+ 1976, 11, "M11", 18, 14, 23, 30, 123, 460, 0,
+ "rounds to an increment of microseconds"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.round({ smallestUnit: "nanosecond", roundingIncrement: 10 }),
+ 1976, 11, "M11", 18, 14, 23, 30, 123, 456, 790,
+ "rounds to an increment of nanoseconds"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-ceil.js
new file mode 100644
index 0000000000..6363a0b9f0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-ceil.js
@@ -0,0 +1,35 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Tests calculations with roundingMode "ceil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 987, 500);
+
+const expected = [
+ ["day", [1976, 11, 'M11', 19]],
+ ["hour", [1976, 11, 'M11', 18, 15]],
+ ["minute", [1976, 11, 'M11', 18, 14, 24]],
+ ["second", [1976, 11, 'M11', 18, 14, 23, 31]],
+ ["millisecond", [1976, 11, 'M11', 18, 14, 23, 30, 124]],
+ ["microsecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 988]],
+ ["nanosecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 987, 500]],
+];
+
+const roundingMode = "ceil";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [y, mon, mc, d, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainDateTime(
+ instance.round({ smallestUnit, roundingMode }),
+ y, mon, mc, d, h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-expand.js
new file mode 100644
index 0000000000..00b8ab4742
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-expand.js
@@ -0,0 +1,35 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Tests calculations with roundingMode "expand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 987, 500);
+
+const expected = [
+ ["day", [1976, 11, 'M11', 19]],
+ ["hour", [1976, 11, 'M11', 18, 15]],
+ ["minute", [1976, 11, 'M11', 18, 14, 24]],
+ ["second", [1976, 11, 'M11', 18, 14, 23, 31]],
+ ["millisecond", [1976, 11, 'M11', 18, 14, 23, 30, 124]],
+ ["microsecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 988]],
+ ["nanosecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 987, 500]],
+];
+
+const roundingMode = "expand";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [y, mon, mc, d, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainDateTime(
+ instance.round({ smallestUnit, roundingMode }),
+ y, mon, mc, d, h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-floor.js
new file mode 100644
index 0000000000..1a70fe0c2c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-floor.js
@@ -0,0 +1,35 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Tests calculations with roundingMode "floor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 987, 500);
+
+const expected = [
+ ["day", [1976, 11, 'M11', 18]],
+ ["hour", [1976, 11, 'M11', 18, 14]],
+ ["minute", [1976, 11, 'M11', 18, 14, 23]],
+ ["second", [1976, 11, 'M11', 18, 14, 23, 30]],
+ ["millisecond", [1976, 11, 'M11', 18, 14, 23, 30, 123]],
+ ["microsecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 987]],
+ ["nanosecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 987, 500]],
+];
+
+const roundingMode = "floor";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [y, mon, mc, d, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainDateTime(
+ instance.round({ smallestUnit, roundingMode }),
+ y, mon, mc, d, h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..57a84b3cd6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfCeil.js
@@ -0,0 +1,35 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Tests calculations with roundingMode "halfCeil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 987, 500);
+
+const expected = [
+ ["day", [1976, 11, 'M11', 19]],
+ ["hour", [1976, 11, 'M11', 18, 14]],
+ ["minute", [1976, 11, 'M11', 18, 14, 24]],
+ ["second", [1976, 11, 'M11', 18, 14, 23, 30]],
+ ["millisecond", [1976, 11, 'M11', 18, 14, 23, 30, 124]],
+ ["microsecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 988]],
+ ["nanosecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 987, 500]],
+];
+
+const roundingMode = "halfCeil";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [y, mon, mc, d, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainDateTime(
+ instance.round({ smallestUnit, roundingMode }),
+ y, mon, mc, d, h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfEven.js
new file mode 100644
index 0000000000..b65c21d152
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfEven.js
@@ -0,0 +1,35 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Tests calculations with roundingMode "halfEven".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 987, 500);
+
+const expected = [
+ ["day", [1976, 11, 'M11', 19]],
+ ["hour", [1976, 11, 'M11', 18, 14]],
+ ["minute", [1976, 11, 'M11', 18, 14, 24]],
+ ["second", [1976, 11, 'M11', 18, 14, 23, 30]],
+ ["millisecond", [1976, 11, 'M11', 18, 14, 23, 30, 124]],
+ ["microsecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 988]],
+ ["nanosecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 987, 500]],
+];
+
+const roundingMode = "halfEven";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [y, mon, mc, d, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainDateTime(
+ instance.round({ smallestUnit, roundingMode }),
+ y, mon, mc, d, h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..8e5c31cc24
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfExpand.js
@@ -0,0 +1,35 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Tests calculations with roundingMode "halfExpand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 987, 500);
+
+const expected = [
+ ["day", [1976, 11, 'M11', 19]],
+ ["hour", [1976, 11, 'M11', 18, 14]],
+ ["minute", [1976, 11, 'M11', 18, 14, 24]],
+ ["second", [1976, 11, 'M11', 18, 14, 23, 30]],
+ ["millisecond", [1976, 11, 'M11', 18, 14, 23, 30, 124]],
+ ["microsecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 988]],
+ ["nanosecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 987, 500]],
+];
+
+const roundingMode = "halfExpand";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [y, mon, mc, d, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainDateTime(
+ instance.round({ smallestUnit, roundingMode }),
+ y, mon, mc, d, h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..9eb15ee2a3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfFloor.js
@@ -0,0 +1,35 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Tests calculations with roundingMode "halfFloor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 987, 500);
+
+const expected = [
+ ["day", [1976, 11, 'M11', 19]],
+ ["hour", [1976, 11, 'M11', 18, 14]],
+ ["minute", [1976, 11, 'M11', 18, 14, 24]],
+ ["second", [1976, 11, 'M11', 18, 14, 23, 30]],
+ ["millisecond", [1976, 11, 'M11', 18, 14, 23, 30, 124]],
+ ["microsecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 987]],
+ ["nanosecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 987, 500]],
+];
+
+const roundingMode = "halfFloor";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [y, mon, mc, d, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainDateTime(
+ instance.round({ smallestUnit, roundingMode }),
+ y, mon, mc, d, h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..7a6534d351
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfTrunc.js
@@ -0,0 +1,35 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Tests calculations with roundingMode "halfTrunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 987, 500);
+
+const expected = [
+ ["day", [1976, 11, 'M11', 19]],
+ ["hour", [1976, 11, 'M11', 18, 14]],
+ ["minute", [1976, 11, 'M11', 18, 14, 24]],
+ ["second", [1976, 11, 'M11', 18, 14, 23, 30]],
+ ["millisecond", [1976, 11, 'M11', 18, 14, 23, 30, 124]],
+ ["microsecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 987]],
+ ["nanosecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 987, 500]],
+];
+
+const roundingMode = "halfTrunc";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [y, mon, mc, d, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainDateTime(
+ instance.round({ smallestUnit, roundingMode }),
+ y, mon, mc, d, h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfexpand-is-default.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfexpand-is-default.js
new file mode 100644
index 0000000000..5a66171d04
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfexpand-is-default.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Half-expand is the default rounding mode
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 456, 789);
+
+const units = {
+ "day": [1976, 11, "M11", 19, 0, 0, 0, 0, 0, 0],
+ "hour": [1976, 11, "M11", 18, 14, 0, 0, 0, 0, 0],
+ "minute": [1976, 11, "M11", 18, 14, 24, 0, 0, 0, 0],
+ "second": [1976, 11, "M11", 18, 14, 23, 30, 0, 0, 0],
+ "millisecond": [1976, 11, "M11", 18, 14, 23, 30, 123, 0, 0],
+ "microsecond": [1976, 11, "M11", 18, 14, 23, 30, 123, 457, 0],
+ "nanosecond": [1976, 11, "M11", 18, 14, 23, 30, 123, 456, 789]
+};
+
+const expected = [1976, 11, "M11", 18, 0, 0, 0, 0, 0, 0];
+
+Object.entries(units).forEach(([unit, expected]) => {
+ TemporalHelpers.assertPlainDateTime(
+ dt.round({ smallestUnit: unit }),
+ ...expected,
+ `halfExpand is the default (smallest unit = ${unit}, rounding mode absent)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..8bb99a0d7e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-invalid-string.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) {
+ assert.throws(RangeError, () => datetime.round({ smallestUnit: "microsecond", roundingMode }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-trunc.js
new file mode 100644
index 0000000000..51634108db
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-trunc.js
@@ -0,0 +1,35 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Tests calculations with roundingMode "trunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 987, 500);
+
+const expected = [
+ ["day", [1976, 11, 'M11', 18]],
+ ["hour", [1976, 11, 'M11', 18, 14]],
+ ["minute", [1976, 11, 'M11', 18, 14, 23]],
+ ["second", [1976, 11, 'M11', 18, 14, 23, 30]],
+ ["millisecond", [1976, 11, 'M11', 18, 14, 23, 30, 123]],
+ ["microsecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 987]],
+ ["nanosecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 987, 500]],
+];
+
+const roundingMode = "trunc";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [y, mon, mc, d, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainDateTime(
+ instance.round({ smallestUnit, roundingMode }),
+ y, mon, mc, d, h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-undefined.js
new file mode 100644
index 0000000000..852164e980
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-undefined.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+
+const explicit1 = datetime.round({ smallestUnit: "microsecond", roundingMode: undefined });
+TemporalHelpers.assertPlainDateTime(explicit1, 2000, 5, "M05", 2, 12, 34, 56, 123, 988, 0, "default roundingMode is halfExpand");
+const implicit1 = datetime.round({ smallestUnit: "microsecond" });
+TemporalHelpers.assertPlainDateTime(implicit1, 2000, 5, "M05", 2, 12, 34, 56, 123, 988, 0, "default roundingMode is halfExpand");
+
+const explicit2 = datetime.round({ smallestUnit: "millisecond", roundingMode: undefined });
+TemporalHelpers.assertPlainDateTime(explicit2, 2000, 5, "M05", 2, 12, 34, 56, 124, 0, 0, "default roundingMode is halfExpand");
+const implicit2 = datetime.round({ smallestUnit: "millisecond" });
+TemporalHelpers.assertPlainDateTime(implicit2, 2000, 5, "M05", 2, 12, 34, 56, 124, 0, 0, "default roundingMode is halfExpand");
+
+const explicit3 = datetime.round({ smallestUnit: "second", roundingMode: undefined });
+TemporalHelpers.assertPlainDateTime(explicit3, 2000, 5, "M05", 2, 12, 34, 56, 0, 0, 0, "default roundingMode is halfExpand");
+const implicit3 = datetime.round({ smallestUnit: "second" });
+TemporalHelpers.assertPlainDateTime(implicit3, 2000, 5, "M05", 2, 12, 34, 56, 0, 0, 0, "default roundingMode is halfExpand");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..58da6f97c2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-wrong-type.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "halfExpand",
+ (roundingMode) => datetime.round({ smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 56, 123, 988, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundto-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundto-invalid-string.js
new file mode 100644
index 0000000000..e466c5c37a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundto-invalid-string.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+const badValues = [
+ "era",
+ "eraYear",
+ "year",
+ "month",
+ "week",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "years",
+ "months",
+ "weeks",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => datetime.round(smallestUnit),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..fe08dcf32f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-invalid-string.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+const badValues = [
+ "era",
+ "eraYear",
+ "year",
+ "month",
+ "week",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "years",
+ "months",
+ "weeks",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => datetime.round({ smallestUnit }),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..5883d38140
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-plurals-accepted.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 789, 999, 999);
+const validUnits = [
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => datetime.round({ smallestUnit }), validUnits);
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => datetime.round(smallestUnit), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-string-shorthand.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-string-shorthand.js
new file mode 100644
index 0000000000..98e903dc48
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-string-shorthand.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: String as first argument is equivalent to options bag with smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 789, 999, 999);
+const validUnits = [
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+validUnits.forEach((smallestUnit) => {
+ const full = instance.round({ smallestUnit });
+ const shorthand = instance.round(smallestUnit);
+ TemporalHelpers.assertPlainDateTimesEqual(shorthand, full, `"${smallestUnit}" as first argument to round is equivalent to options bag`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..58438c8163
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-wrong-type.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => datetime.round({ smallestUnit }),
+ (result, descr) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 56, 123, 988, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/subclassing-ignored.js
new file mode 100644
index 0000000000..1a4fad9078
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/subclassing-ignored.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDateTime,
+ [2000, 5, 2, 12, 34, 56, 987, 654, 321],
+ "round",
+ [{ smallestUnit: 'second' }],
+ (result) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 57, 0, 0, 0),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-argument-object-insufficient-data.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-argument-object-insufficient-data.js
new file mode 100644
index 0000000000..5a78bfc8e6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-argument-object-insufficient-data.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Throw if smallest unit is missing from argument
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 456, 789);
+
+assert.throws(
+ RangeError,
+ () => dt.round({ roundingIncrement: 1, roundingMode: "ceil" }),
+ "throws without required smallestUnit parameter"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-argument-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-argument-object.js
new file mode 100644
index 0000000000..3087ab282a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-argument-object.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Throw if argument is an empty plain object
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 456, 789);
+
+assert.throws(
+ RangeError,
+ () => dt.round({}),
+ "throws on empty object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-no-argument.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-no-argument.js
new file mode 100644
index 0000000000..7d3b5cc82b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-no-argument.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Throw if no arguments at all are given
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 456, 789);
+
+assert.throws(
+ TypeError,
+ () => dt.round(),
+ "throws without any parameters"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-undefined.js
new file mode 100644
index 0000000000..3e1e32fc93
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-undefined.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Throw if sole argument is undefined
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 456, 789);
+
+assert.throws(
+ TypeError,
+ () => dt.round(undefined),
+ "throws without undefined as sole parameter"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/branding.js
new file mode 100644
index 0000000000..8b168faf11
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.second
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const second = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "second").get;
+
+assert.sameValue(typeof second, "function");
+
+assert.throws(TypeError, () => second.call(undefined), "undefined");
+assert.throws(TypeError, () => second.call(null), "null");
+assert.throws(TypeError, () => second.call(true), "true");
+assert.throws(TypeError, () => second.call(""), "empty string");
+assert.throws(TypeError, () => second.call(Symbol()), "symbol");
+assert.throws(TypeError, () => second.call(1), "1");
+assert.throws(TypeError, () => second.call({}), "plain object");
+assert.throws(TypeError, () => second.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => second.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/prop-desc.js
new file mode 100644
index 0000000000..ebed5d7c63
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.second
+description: The "second" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "second");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..5ef369ced3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const arg = { year: 2000, month: 5, day: 2, hour: 21, minute: 43, second: 5, calendar: "iso8601" };
+instance.since(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-number.js
new file mode 100644
index 0000000000..ba9ca1e11a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-number.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: A number cannot be used in place of a Temporal.PlainDateTime
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.since(arg),
+ `A number (${arg}) is not a valid ISO string for PlainDateTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-object.js
new file mode 100644
index 0000000000..57b3654f02
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-object.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Plain objects are accepted as an argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+
+TemporalHelpers.assertDuration(
+ dt.since({ year: 2019, month: 10, day: 29, hour: 10 }),
+ 0, 0, 0, -15684, -18, -36, -29, -876, -543, -211,
+ "casts argument (plain object)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-plaindate.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-plaindate.js
new file mode 100644
index 0000000000..07fc4c0423
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-plaindate.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Fast path for converting Temporal.PlainDate to Temporal.PlainDateTime by reading internal slots
+info: |
+ sec-temporal.plaindatetime.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.b:
+ b. If _item_ has an [[InitializedTemporalDate]] internal slot, then
+ i. Return ? CreateTemporalDateTime(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], 0, 0, 0, 0, 0, 0, _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalPlainDateTimeFastPath((date, calendar) => {
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 0, 0, 0, 987, 654, 321, calendar);
+ const result = datetime.since(date);
+ assert.sameValue(result.total({ unit: "nanoseconds" }), 987654321, "PlainDate is converted to midnight");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..d35b163b19
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: The calendar name is case-insensitive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18);
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.since(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..1a344939c7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Leap second is a valid ISO string for a calendar in a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18);
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.since(arg);
+TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..9c7f65ec05
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-number.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.since(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..a4a4e10cd5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-string.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18);
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.since(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..d3a8e5aba7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.since(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.since(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..6cd66acaf2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-year-zero.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..2fa40a1aee
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-calendar-annotation.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[u-ca=iso8601]", "without time zone"],
+ ["1976-11-18T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["1976-11-18T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["1976-11-18T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["1976-11-18T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.since(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..b0b311f210
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..017213c52c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-date-with-utc-offset.js
@@ -0,0 +1,49 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+const validStrings = [
+ "1976-11-18T15:23+00:00",
+ "1976-11-18T15:23+00:00[UTC]",
+ "1976-11-18T15:23+00:00[!UTC]",
+ "1976-11-18T15:23-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.since(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `"${arg}" is a valid UTC offset with time for PlainDateTime`
+ );
+}
+
+const invalidStrings = [
+ "2022-09-15Z",
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDateTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..6f358f3325
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-multiple-calendar.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..c99a4af9c7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-multiple-time-zone.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[UTC][UTC]",
+ "1970-01-01T00:00[!UTC][UTC]",
+ "1970-01-01T00:00[UTC][!UTC]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00[UTC][foo=bar][UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-time-separators.js
new file mode 100644
index 0000000000..0f15f572a4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-time-separators.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23", "uppercase T"],
+ ["1976-11-18t15:23", "lowercase T"],
+ ["1976-11-18 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.since(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..81a90f173c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-time-zone-annotation.js
@@ -0,0 +1,35 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[Asia/Kolkata]", "named, with no offset"],
+ ["1976-11-18T15:23[!Europe/Vienna]", "named, with ! and no offset"],
+ ["1976-11-18T15:23[+00:00]", "numeric, with no offset"],
+ ["1976-11-18T15:23[!-02:30]", "numeric, with ! and no offset"],
+ ["1976-11-18T15:23+00:00[UTC]", "named, with offset"],
+ ["1976-11-18T15:23+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["1976-11-18T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["1976-11-18T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.since(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..3960259916
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-unknown-annotation.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[foo=bar]", "alone"],
+ ["1976-11-18T15:23[UTC][foo=bar]", "with time zone"],
+ ["1976-11-18T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["1976-11-18T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["1976-11-18T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.since(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..cc0b2f2449
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-with-utc-designator.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: RangeError thrown if a string with UTC designator is used as a PlainDateTime
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ "String with UTC designator should not be valid as a PlainDateTime"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string.js
new file mode 100644
index 0000000000..f1d3237a1e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Date-like string arguments are acceptable
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+
+TemporalHelpers.assertDuration(
+ dt.since("2019-10-29T10:46:38.271986102"),
+ 0, 0, 0, -15684, -19, -23, -8, -148, -529, -313,
+ "casts argument (string)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-wrong-type.js
new file mode 100644
index 0000000000..fb55c45f60
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-wrong-type.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDateTime
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => instance.since(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDateTime, "Temporal.PlainDateTime, object"],
+ [Temporal.PlainDateTime.prototype, "Temporal.PlainDateTime.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.since(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 0000000000..803747b39b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,46 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Negative time fields are balanced upwards if the argument is given as ZonedDateTime
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-temporal-totemporaldatetime step 3.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ ii. 1. Return ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
+ sec-temporal.plaindatetime.prototype.since step 3:
+ 3. Set _other_ ? ToTemporalDateTime(_other_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// This code path is encountered if the time zone offset is negative and its
+// absolute value in nanoseconds is greater than the nanosecond field of the
+// exact time's epoch parts
+const tz = TemporalHelpers.specificOffsetTimeZone(-2);
+const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
+
+const diff = new Temporal.PlainDateTime(1970, 1, 1).since(datetime);
+
+TemporalHelpers.assertDuration(diff, 0, 0, 0, 0, -1, -1, -1, -1, 0, -999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..a128e5820b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+const result = instance.since(datetime);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 11239, 22, 40, 10, 987, 654, 320);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..76738f0272
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.since(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..1fd51aa6b3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => plain.since(zoned),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..0838e28752
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.since(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..c9fcd949d7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => plain.since(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/balance-negative-duration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/balance-negative-duration.js
new file mode 100644
index 0000000000..901ede1927
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/balance-negative-duration.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Negative durations are balanced correctly by the modulo operation in NanosecondsToDays
+info: |
+ sec-temporal-nanosecondstodays step 6:
+ 6. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Return the new Record { ..., [[Nanoseconds]]: abs(_nanoseconds_) modulo _dayLengthNs_ × _sign_, ... }.
+ sec-temporal-balanceduration step 4:
+ 4. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_).
+ sec-temporal-differenceisodatetime steps 7 and 13:
+ 7. If _timeSign_ is -_dateSign_, then
+ ...
+ b. Set _timeDifference_ to ? BalanceDuration(-_timeSign_, _timeDifference_.[[Hours]], _timeDifference_.[[Minutes]], _timeDifference_.[[Seconds]], _timeDifference_.[[Milliseconds]], _timeDifference_.[[Microseconds]], _timeDifference_.[[Nanoseconds]], _largestUnit_).
+ ...
+ 16. Return ? BalanceDuration(_dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], _dateDifference_.[[Days]], _timeDifference_.[[Hours]], _timeDifference_.[[Minutes]], _timeDifference_.[[Seconds]], _timeDifference_.[[Milliseconds]], _timeDifference_.[[Microseconds]], _timeDifference_.[[Nanoseconds]], _largestUnit_).
+ sec-temporal.plaindatetime.prototype.since step 14:
+ 14. Let _diff_ be ? DifferenceISODateTime(_other_.[[ISOYear]], _other_.[[ISOMonth]], _other_.[[ISODay]], _other_.[[ISOHour]], _other_.[[ISOMinute]], _other_.[[ISOSecond]], _other_.[[ISOMillisecond]], _other_.[[ISOMicrosecond]], _other_.[[ISONanosecond]], _dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], _largestUnit_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier1 = new Temporal.PlainDateTime(2000, 5, 2, 9);
+const later1 = new Temporal.PlainDateTime(2000, 5, 5, 10);
+const result1 = later1.since(earlier1, { largestUnit: 'day' });
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 3, 1, 0, 0, 0, 0, 0, "date sign == time sign");
+
+const earlier2 = new Temporal.PlainDateTime(2000, 5, 2, 10);
+const later2 = new Temporal.PlainDateTime(2000, 5, 5, 9);
+const result2 = later2.since(earlier2, { largestUnit: 'day' });
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 2, 23, 0, 0, 0, 0, 0, "date sign != time sign");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/balance-negative-time-units.js
new file mode 100644
index 0000000000..d4f1df6ea2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/balance-negative-time-units.js
@@ -0,0 +1,53 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-differencetime step 8:
+ 8. Let _bt_ be ? BalanceTime(_hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
+ sec-temporal-differenceisodatetime step 2:
+ 2. Let _timeDifference_ be ? DifferenceTime(_h1_, _min1_, _s1_, _ms1_, _mus1_, _ns1_, _h2_, _min2_, _s2_, _ms2_, _mus2_, _ns2_).
+ sec-temporal.plaindatetime.prototype.since step 14:
+ 14. Let _diff_ be ? DifferenceISODateTime(_other_.[[ISOYear]], _other_.[[ISOMonth]], _other_.[[ISODay]], _other_.[[ISOHour]], _other_.[[ISOMinute]], _other_.[[ISOSecond]], _other_.[[ISOMillisecond]], _other_.[[ISOMicrosecond]], _other_.[[ISONanosecond]], _dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], _largestUnit_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1996, 5, 2, 1, 1, 1, 1, 1, 1);
+
+const result1 = datetime.since(new Temporal.PlainDateTime(1996, 5, 2, 0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
+
+const result2 = datetime.since(new Temporal.PlainDateTime(1996, 5, 2, 0, 0, 0, 0, 2));
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 1, 1, 1, 0, 999, 1, "microseconds balance");
+
+const result3 = datetime.since(new Temporal.PlainDateTime(1996, 5, 2, 0, 0, 0, 2));
+TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 1, 1, 0, 999, 1, 1, "milliseconds balance");
+
+const result4 = datetime.since(new Temporal.PlainDateTime(1996, 5, 2, 0, 0, 2));
+TemporalHelpers.assertDuration(result4, 0, 0, 0, 0, 1, 0, 59, 1, 1, 1, "seconds balance");
+
+const result5 = datetime.since(new Temporal.PlainDateTime(1996, 5, 2, 0, 2));
+TemporalHelpers.assertDuration(result5, 0, 0, 0, 0, 0, 59, 1, 1, 1, 1, "minutes balance");
+
+// This one is different because hours are later balanced again in BalanceDuration
+const result6 = datetime.since(new Temporal.PlainDateTime(1996, 5, 2, 2));
+TemporalHelpers.assertDuration(result6, 0, 0, 0, 0, 0, -58, -58, -998, -998, -999, "hours balance");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/branding.js
new file mode 100644
index 0000000000..e5d37d2d5b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const since = Temporal.PlainDateTime.prototype.since;
+
+assert.sameValue(typeof since, "function");
+
+const args = [new Temporal.PlainDateTime(2022, 6, 22)];
+
+assert.throws(TypeError, () => since.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => since.apply(null, args), "null");
+assert.throws(TypeError, () => since.apply(true, args), "true");
+assert.throws(TypeError, () => since.apply("", args), "empty string");
+assert.throws(TypeError, () => since.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => since.apply(1, args), "1");
+assert.throws(TypeError, () => since.apply({}, args), "plain object");
+assert.throws(TypeError, () => since.apply(Temporal.PlainDateTime, args), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => since.apply(Temporal.PlainDateTime.prototype, args), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..8645aec4f9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateUntilOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateUntil");
+Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateUntil should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.since(new Temporal.PlainDateTime(1999, 4, 1));
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", dateUntilOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/builtin.js
new file mode 100644
index 0000000000..b559811c5a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: >
+ Tests that Temporal.PlainDateTime.prototype.since
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.since),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.since),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.since),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.since.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateadd-called-with-plaindate-instance.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateadd-called-with-plaindate-instance.js
new file mode 100644
index 0000000000..e50a5121fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateadd-called-with-plaindate-instance.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: >
+ relativeTo parameters that are not ZonedDateTime or undefined, are always
+ converted to PlainDate for observable calendar calls
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddPlainDateInstance();
+const instance = new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, 0, 0, calendar);
+instance.since(new Temporal.PlainDateTime(2000, 5, 2, 0, 0, 0, 0, 0, 0, calendar), { smallestUnit: "month" });
+assert(calendar.dateAddCallCount > 0, "assertions in calendar.dateAdd() should have been tested");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..c7e9451467
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-datefromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.since(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-copy-of-options.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-copy-of-options.js
new file mode 100644
index 0000000000..25a93951fb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-copy-of-options.js
@@ -0,0 +1,35 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: The dateUntil() method on the calendar is called with a copy of the options bag
+features: [Temporal]
+---*/
+
+const originalOptions = {
+ largestUnit: "year",
+ shouldBeCopied: {},
+};
+let called = false;
+
+class Calendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateUntil(d1, d2, options) {
+ called = true;
+ assert.notSameValue(options, originalOptions, "options bag should be a copy");
+ assert.sameValue(options.shouldBeCopied, originalOptions.shouldBeCopied, "options bag should be a shallow copy");
+ return new Temporal.Duration();
+ }
+}
+const calendar = new Calendar();
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322, calendar);
+earlier.since(later, originalOptions);
+assert(called, "calendar.dateUntil must be called");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js
new file mode 100644
index 0000000000..58d5037862
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: >
+ Calendar.dateUntil method is called with a null-prototype object as the
+ options value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckOptionsPrototypePollution();
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+const argument = new Temporal.PlainDateTime(2022, 6, 14, 18, 21, 36, 660, 690, 387, calendar);
+instance.since(argument, { largestUnit: "months" });
+assert.sameValue(calendar.dateUntilCallCount, 1, "dateUntil should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-plaindate-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-plaindate-calendar.js
new file mode 100644
index 0000000000..cdb2988218
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-plaindate-calendar.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: calendar.dateUntil() is passed PlainDate objects with the receiver's calendar
+info: |
+ DifferenceISODateTime ( y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1, y2, mon2, d2, h2, min2, s2, ms2, mus2, ns2, calendar, largestUnit [ , options ] )
+
+ 8. Let _date1_ be ? CreateTemporalDate(_balanceResult_.[[Year]], _balanceResult_.[[Month]], _balanceResult_.[[Day]], _calendar_).
+ 9. Let _date2_ be ? CreateTemporalDate(_y2_, _mon2_, _d2_, _calendar_).
+ 12. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
+features: [Temporal]
+---*/
+
+class Calendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateUntil(d1, d2) {
+ assert.sameValue(d1.getCalendar(), this, "d1.calendar");
+ assert.sameValue(d2.getCalendar(), this, "d2.calendar");
+ return new Temporal.Duration();
+ }
+}
+const calendar = new Calendar();
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322, calendar);
+const result = earlier.since(later);
+assert(result instanceof Temporal.Duration, "result");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 0000000000..fbcd184fc1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.plaindatetime.prototype.since step 14:
+ 14. Let _diff_ be ? DifferenceISODateTime(_other_.[[ISOYear]], _other_.[[ISOMonth]], _other_.[[ISODay]], _other_.[[ISOHour]], _other_.[[ISOMinute]], _other_.[[ISOSecond]], _other_.[[ISOMillisecond]], _other_.[[ISOMicrosecond]], _other_.[[ISONanosecond]], _dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], _largestUnit_, _options_).
+ sec-temporal-differenceisodatetime steps 9–11:
+ 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
+ 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_).
+ 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+ const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322, calendar);
+ later.since(earlier, { largestUnit });
+ },
+ {
+ years: ["year"],
+ months: ["month"],
+ weeks: ["week"],
+ days: [],
+ hours: [],
+ minutes: [],
+ seconds: [],
+ milliseconds: [],
+ microseconds: [],
+ nanoseconds: []
+ }
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-fields-iterable.js
new file mode 100644
index 0000000000..36e4e4ee78
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-fields-iterable.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindatetime.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+datetime.since({ year: 2005, month: 6, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-temporal-object.js
new file mode 100644
index 0000000000..6a264c5b07
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindatetime.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, temporalObject);
+ datetime.since({ year: 2005, month: 6, day: 2, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..051d7f7fce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/constructor-in-calendar-fields.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+assert.throws(RangeError, () => instance.since(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/different-calendars-throws.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/different-calendars-throws.js
new file mode 100644
index 0000000000..3ea7e14b34
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/different-calendars-throws.js
@@ -0,0 +1,42 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Fail if the argument is a PlainDateTime with a different calendar
+features: [Temporal]
+---*/
+
+const dt1 = new Temporal.PlainDateTime(2000, 1, 1, 0, 0, 0, 0, 0, 0);
+const dt2 = new Temporal.PlainDateTime(2000, 1, 1, 0, 0, 0, 0, 0, 0, {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "custom",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+
+assert.throws(
+ RangeError,
+ () => dt1.since(dt2),
+ "different calendars not allowed"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..5a714170e6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['hour'], ['microsecond'], ['millisecond'], ['minute'], ['month'], ['monthCode'], ['nanosecond'], ['second'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+ assert.throws(RangeError, () => instance.since(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..1845f4ccc2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/infinity-throws-rangeerror.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindatetime.prototype.since
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15);
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.since({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.since({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-invalid-string.js
new file mode 100644
index 0000000000..e67796f959
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-invalid-string.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 987, 654, 321);
+const badValues = [
+ "era",
+ "eraYear",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string"
+];
+for (const largestUnit of badValues) {
+ assert.throws(RangeError, () => later.since(earlier, { largestUnit }),
+ `"${largestUnit}" is not a valid value for largestUnit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-plurals-accepted.js
new file mode 100644
index 0000000000..df1ef728cc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-plurals-accepted.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 12, 13, 35, 57, 988, 655, 322);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => later.since(earlier, { largestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-smallestunit-mismatch.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-smallestunit-mismatch.js
new file mode 100644
index 0000000000..4b902d0f69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-smallestunit-mismatch.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: RangeError thrown when smallestUnit is larger than largestUnit
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 987, 654, 321);
+const units = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+for (let largestIdx = 1; largestIdx < units.length; largestIdx++) {
+ for (let smallestIdx = 0; smallestIdx < largestIdx; smallestIdx++) {
+ const largestUnit = units[largestIdx];
+ const smallestUnit = units[smallestIdx];
+ assert.throws(RangeError, () => later.since(earlier, { largestUnit, smallestUnit }));
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-undefined.js
new file mode 100644
index 0000000000..780b25118f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-undefined.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Fallback value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 987, 654, 321);
+
+const explicit = later.since(earlier, { largestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 397, 1, 1, 1, 987, 654, 321, "default largestUnit is day");
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 397, 1, 1, 1, 987, 654, 321, "default largestUnit is day");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-wrong-type.js
new file mode 100644
index 0000000000..12f194cd64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-wrong-type.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 987, 654, 321);
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "year",
+ (largestUnit) => later.since(earlier, { largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 1, 0, 1, 1, 1, 1, 987, 654, 321, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit.js
new file mode 100644
index 0000000000..852a7b8068
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Specify behavior of PlainDateTime.since when largest specified unit is years or months.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+const lastFeb20 = new Temporal.PlainDateTime(2020, 2, 20, 5, 45, 20);
+const lastFeb21 = new Temporal.PlainDateTime(2021, 2, 21, 17, 18, 57);
+TemporalHelpers.assertDuration(lastFeb21.since(lastFeb20), 0, 0, 0, 367, 11, 33, 37, 0, 0, 0, 'does not include higher units than necessary (largest unit unspecified)');
+TemporalHelpers.assertDuration(lastFeb21.since(lastFeb20, { largestUnit: 'months' }), 0, 12, 0, 1, 11, 33, 37, 0, 0, 0, 'does not include higher units than necessary (largest unit is months)');
+TemporalHelpers.assertDuration(lastFeb21.since(lastFeb20, { largestUnit: 'years' }), 1, 0, 0, 1, 11, 33, 37, 0, 0, 0, 'does not include higher units than necessary (largest unit is years)');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/leap-second.js
new file mode 100644
index 0000000000..62e2963d22
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/leap-second.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Leap second is a valid ISO string for PlainDateTime
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2016, 12, 31, 23, 59, 59);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.since(arg);
+TemporalHelpers.assertDuration(
+ result1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for PlainDateTime"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.since(arg);
+TemporalHelpers.assertDuration(
+ result2,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "second: 60 is ignored in property bag for PlainDateTime"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/length.js
new file mode 100644
index 0000000000..944b5458a9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Temporal.PlainDateTime.prototype.since.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.since, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/name.js
new file mode 100644
index 0000000000..eb45ee1e00
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Temporal.PlainDateTime.prototype.since.name is "since".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.since, "name", {
+ value: "since",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/no-unnecessary-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/no-unnecessary-units.js
new file mode 100644
index 0000000000..62ec2d003e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/no-unnecessary-units.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Do not return Durations with unnecessary units
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const feb2 = new Temporal.PlainDateTime(2020, 2, 2, 0, 0);
+const feb28 = new Temporal.PlainDateTime(2021, 2, 28, 0, 0);
+
+TemporalHelpers.assertDuration(
+ feb28.since(feb2),
+ 0, 0, 0, 392, 0, 0, 0, 0, 0, 0,
+ "does not include higher units than necessary (no largest unit)"
+);
+
+TemporalHelpers.assertDuration(
+ feb28.since(feb2, { largestUnit: "months" }),
+ 0, 12, 0, 26, 0, 0, 0, 0, 0, 0,
+ "does not include higher units than necessary (largest unit = months)"
+);
+
+TemporalHelpers.assertDuration(
+ feb28.since(feb2, { largestUnit: "years" }),
+ 1, 0, 0, 26, 0, 0, 0, 0, 0, 0,
+ "does not include higher units than necessary (largest unit = years)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/not-a-constructor.js
new file mode 100644
index 0000000000..e1288d502d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: >
+ Temporal.PlainDateTime.prototype.since does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.since();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.since), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.since)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-empty.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-empty.js
new file mode 100644
index 0000000000..8044a80ed1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-empty.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Empty objects are acceptable
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const feb20 = new Temporal.PlainDateTime(2020, 2, 1, 0, 0);
+const feb21 = new Temporal.PlainDateTime(2021, 2, 1, 0, 0);
+
+TemporalHelpers.assertDuration(
+ feb21.since(feb20, {}),
+ 0, 0, 0, 366, 0, 0, 0, 0, 0, 0,
+ "empty plain object options"
+);
+
+TemporalHelpers.assertDuration(
+ feb21.since(feb20, () => {}),
+ 0, 0, 0, 366, 0, 0, 0, 0, 0, 0,
+ "empty function object options"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-invalid.js
new file mode 100644
index 0000000000..8e410662d2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-invalid.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: A variety of bad options (type error thrown)
+features: [Temporal, Symbol]
+---*/
+
+const feb20 = new Temporal.PlainDateTime(2020, 2, 1, 0, 0);
+const feb21 = new Temporal.PlainDateTime(2021, 2, 1, 0, 0);
+
+const badOptions = [null, 1, 'hello', true, Symbol('foo'), 1n];
+badOptions.forEach((bad) => {
+ assert.throws(
+ TypeError,
+ () => feb21.since(feb20, bad),
+ `bad options (${typeof bad})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-undefined.js
new file mode 100644
index 0000000000..a5334b31d6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-undefined.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2000, 6, 12, 12, 34, 56, 987, 654, 322);
+
+const explicit = later.since(earlier, undefined);
+assert.sameValue(explicit.years, 0, "default largest unit is days");
+assert.sameValue(explicit.months, 0, "default largest unit is days");
+assert.sameValue(explicit.weeks, 0, "default largest unit is days");
+assert.sameValue(explicit.days, 41, "default largest unit is days");
+assert.sameValue(explicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding");
+
+const implicit = later.since(earlier);
+assert.sameValue(implicit.years, 0, "default largest unit is days");
+assert.sameValue(implicit.months, 0, "default largest unit is days");
+assert.sameValue(implicit.weeks, 0, "default largest unit is days");
+assert.sameValue(implicit.days, 41, "default largest unit is days");
+assert.sameValue(implicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-wrong-type.js
new file mode 100644
index 0000000000..4a19f0ef5f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-wrong-type.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.since(new Temporal.PlainDateTime(1976, 11, 18), value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/order-of-operations.js
new file mode 100644
index 0000000000..aaeca20a9b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/order-of-operations.js
@@ -0,0 +1,250 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Properties on objects passed to since() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalDateTime
+ "get other.calendar",
+ "has other.calendar.dateAdd",
+ "has other.calendar.dateFromFields",
+ "has other.calendar.dateUntil",
+ "has other.calendar.day",
+ "has other.calendar.dayOfWeek",
+ "has other.calendar.dayOfYear",
+ "has other.calendar.daysInMonth",
+ "has other.calendar.daysInWeek",
+ "has other.calendar.daysInYear",
+ "has other.calendar.fields",
+ "has other.calendar.id",
+ "has other.calendar.inLeapYear",
+ "has other.calendar.mergeFields",
+ "has other.calendar.month",
+ "has other.calendar.monthCode",
+ "has other.calendar.monthDayFromFields",
+ "has other.calendar.monthsInYear",
+ "has other.calendar.weekOfYear",
+ "has other.calendar.year",
+ "has other.calendar.yearMonthFromFields",
+ "has other.calendar.yearOfWeek",
+ "get other.calendar.dateFromFields",
+ "get other.calendar.fields",
+ "call other.calendar.fields",
+ "get other.day",
+ "get other.day.valueOf",
+ "call other.day.valueOf",
+ "get other.hour",
+ "get other.hour.valueOf",
+ "call other.hour.valueOf",
+ "get other.microsecond",
+ "get other.microsecond.valueOf",
+ "call other.microsecond.valueOf",
+ "get other.millisecond",
+ "get other.millisecond.valueOf",
+ "call other.millisecond.valueOf",
+ "get other.minute",
+ "get other.minute.valueOf",
+ "call other.minute.valueOf",
+ "get other.month",
+ "get other.month.valueOf",
+ "call other.month.valueOf",
+ "get other.monthCode",
+ "get other.monthCode.toString",
+ "call other.monthCode.toString",
+ "get other.nanosecond",
+ "get other.nanosecond.valueOf",
+ "call other.nanosecond.valueOf",
+ "get other.second",
+ "get other.second.valueOf",
+ "call other.second.valueOf",
+ "get other.year",
+ "get other.year.valueOf",
+ "call other.year.valueOf",
+ "call other.calendar.dateFromFields",
+ // CalendarEquals
+ "get this.calendar.id",
+ "get other.calendar.id",
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.roundingIncrement",
+ "get options.roundingIncrement",
+ "getOwnPropertyDescriptor options.roundingMode",
+ "get options.roundingMode",
+ "getOwnPropertyDescriptor options.largestUnit",
+ "get options.largestUnit",
+ "getOwnPropertyDescriptor options.smallestUnit",
+ "get options.smallestUnit",
+ "getOwnPropertyDescriptor options.additional",
+ "get options.additional",
+ // GetDifferenceSettings
+ "get options.largestUnit.toString",
+ "call options.largestUnit.toString",
+ "get options.roundingIncrement.valueOf",
+ "call options.roundingIncrement.valueOf",
+ "get options.roundingMode.toString",
+ "call options.roundingMode.toString",
+ "get options.smallestUnit.toString",
+ "call options.smallestUnit.toString",
+];
+const actual = [];
+
+const ownCalendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, ownCalendar);
+
+const otherDateTimePropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 6,
+ monthCode: "M06",
+ day: 2,
+ hour: 1,
+ minute: 46,
+ second: 40,
+ millisecond: 250,
+ microsecond: 500,
+ nanosecond: 750,
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+
+function createOptionsObserver({ smallestUnit = "nanoseconds", largestUnit = "auto", roundingMode = "halfExpand", roundingIncrement = 1 } = {}) {
+ return TemporalHelpers.propertyBagObserver(actual, {
+ // order is significant, due to iterating through properties in order to
+ // copy them to an internal null-prototype object:
+ roundingIncrement,
+ roundingMode,
+ largestUnit,
+ smallestUnit,
+ additional: "property",
+ }, "options");
+}
+
+// clear any observable things that happened while constructing the objects
+actual.splice(0);
+
+// basic order of observable operations with calendar call, without rounding:
+instance.since(otherDateTimePropertyBag, createOptionsObserver({ largestUnit: "years" }));
+assert.compareArray(actual, expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+]), "order of operations");
+actual.splice(0); // clear
+
+// short-circuit for identical objects:
+const identicalPropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 5,
+ monthCode: "M05",
+ day: 2,
+ hour: 12,
+ minute: 34,
+ second: 56,
+ millisecond: 987,
+ microsecond: 654,
+ nanosecond: 321,
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+
+instance.since(identicalPropertyBag, createOptionsObserver());
+assert.compareArray(actual, expected, "order of operations with identical datetimes");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year:
+const expectedOpsForYearRounding = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateAdd", // 12.d
+ "call this.calendar.dateAdd", // 12.f
+ "call this.calendar.dateUntil", // 12.n
+ "call this.calendar.dateAdd", // 12.x MoveRelativeDate
+ // (12.r not called because other units can't add up to >1 year at this point)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 9.c
+ "call this.calendar.dateUntil" // 9.d
+]);
+instance.since(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "years" }));
+assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year and skips a DateUntil call:
+const otherDatePropertyBagSameMonth = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 5,
+ monthCode: "M05",
+ day: 2,
+ hour: 12,
+ minute: 34,
+ second: 56,
+ millisecond: 987,
+ microsecond: 654,
+ nanosecond: 321,
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+const expectedOpsForYearRoundingSameMonth = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateAdd", // 12.d
+ "call this.calendar.dateAdd", // 12.f
+ "call this.calendar.dateAdd", // 12.x MoveRelativeDate
+ // (12.n not called because months and weeks == 0)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 9.c
+ "call this.calendar.dateUntil" // 9.d
+]);
+instance.until(otherDatePropertyBagSameMonth, createOptionsObserver({ smallestUnit: "years" }));
+assert.compareArray(actual, expectedOpsForYearRoundingSameMonth, "order of operations with smallestUnit = years and no excess months/weeks");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest month:
+const expectedOpsForMonthRounding = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateAdd", // 13.c
+ "call this.calendar.dateAdd", // 13.e
+ "call this.calendar.dateUntil", // 13.m
+ "call this.calendar.dateAdd", // 13.w MoveRelativeDate
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 10.d
+ "call this.calendar.dateUntil", // 10.e
+]);
+instance.since(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "months" }));
+assert.compareArray(actual, expectedOpsForMonthRounding, "order of operations with smallestUnit = months");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest week:
+const expectedOpsForWeekRounding = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateUntil", // 14.f
+ "call this.calendar.dateAdd", // 14.p MoveRelativeDate
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 16
+ "call this.calendar.dateUntil", // 17
+]);
+instance.since(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "weeks" }));
+assert.compareArray(actual, expectedOpsForWeekRounding, "order of operations with smallestUnit = weeks");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/prop-desc.js
new file mode 100644
index 0000000000..a6cdb6bb4b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: The "since" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.since,
+ "function",
+ "`typeof PlainDateTime.prototype.since` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "since", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..4c82a08a7a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/proto-in-calendar-fields.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+assert.throws(RangeError, () => instance.since(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/read-time-fields-before-datefromfields.js
new file mode 100644
index 0000000000..5aa93cd273
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/read-time-fields-before-datefromfields.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.plaindatetime.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.e:
+ e. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const datetime = new Temporal.PlainDateTime(2021, 3, 31, 12, 34, 56, 987, 654, 321);
+const duration = datetime.since({ year: 2021, month: 3, day: 31, calendar });
+
+TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, 12, 34, 56, 987, 654, 321);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/returns-days.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/returns-days.js
new file mode 100644
index 0000000000..ce6ac16027
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/returns-days.js
@@ -0,0 +1,47 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Days are the default level of specificity
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const feb_1_2020 = new Temporal.PlainDateTime(2020, 2, 1, 0, 0);
+const feb_1_2021 = new Temporal.PlainDateTime(2021, 2, 1, 0, 0);
+
+TemporalHelpers.assertDuration(
+ feb_1_2021.since(feb_1_2020),
+ 0, 0, 0, 366, 0, 0, 0, 0, 0, 0,
+ "defaults to returning days (no options)"
+);
+
+TemporalHelpers.assertDuration(
+ feb_1_2021.since(feb_1_2020, { largestUnit: "auto" }),
+ 0, 0, 0, 366, 0, 0, 0, 0, 0, 0,
+ "defaults to returning days (largest unit = auto)"
+);
+
+TemporalHelpers.assertDuration(
+ feb_1_2021.since(feb_1_2020, { largestUnit: "days" }),
+ 0, 0, 0, 366, 0, 0, 0, 0, 0, 0,
+ "defaults to returning days (largest unit = days)"
+);
+
+const dt = new Temporal.PlainDateTime(2020, 2, 1, 0, 0, 0, 0, 0, 1);
+
+TemporalHelpers.assertDuration(
+ dt.since(feb_1_2020),
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+ "defaults to returning days (nanosecond)"
+);
+
+TemporalHelpers.assertDuration(
+ feb_1_2021.since(dt),
+ 0, 0, 0, 365, 23, 59, 59, 999, 999, 999,
+ "defaults to returning days (nanosecond)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/round-cross-unit-boundary.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/round-cross-unit-boundary.js
new file mode 100644
index 0000000000..5ce0254b04
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/round-cross-unit-boundary.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Rounding can cross unit boundaries up to largestUnit
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// Date units
+{
+ const earlier = new Temporal.PlainDateTime(2022, 1, 1);
+ const later = new Temporal.PlainDateTime(2023, 12, 25);
+ const duration = earlier.since(later, { largestUnit: "years", smallestUnit: "months", roundingMode: "expand" });
+ TemporalHelpers.assertDuration(duration, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "-1 year -11 months balances to -2 years");
+}
+
+// Time units
+{
+ const earlier = new Temporal.PlainDateTime(2000, 5, 2);
+ const later = new Temporal.PlainDateTime(2000, 5, 2, 1, 59, 59);
+ const duration = earlier.since(later, { largestUnit: "hours", smallestUnit: "minutes", roundingMode: "expand" });
+ TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, "-1:59 balances to -2 hours");
+}
+
+// Both
+{
+ const earlier = new Temporal.PlainDateTime(1970, 1, 1);
+ const later = new Temporal.PlainDateTime(1971, 12, 31, 23, 59, 59, 999, 999, 999);
+ const duration = earlier.since(later, { largestUnit: "years", smallestUnit: "microseconds", roundingMode: "expand" });
+ TemporalHelpers.assertDuration(duration, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "rounding down 1 ns balances to -2 years");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/round-negative-duration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/round-negative-duration.js
new file mode 100644
index 0000000000..33ded673ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/round-negative-duration.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Negative durations are rounded correctly by the modulo operation in NanosecondsToDays
+info: |
+ sec-temporal-nanosecondstodays step 6:
+ 6. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Return the new Record { ..., [[Nanoseconds]]: abs(_nanoseconds_) modulo _dayLengthNs_ × _sign_, ... }.
+ sec-temporal-roundduration step 6:
+ 6. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_).
+ sec-temporal.plaindatetime.prototype.since step 15:
+ 15. Let _roundResult_ be ? RoundDuration(−_diff_.[[Years]], −_diff_.[[Months]], −_diff_.[[Weeks]], −_diff_.[[Days]], −_diff_.[[Hours]], −_diff_.[[Minutes]], −_diff_.[[Seconds]], −_diff_.[[Milliseconds]], −_diff_.[[Microseconds]], −_diff_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _dateTime_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12);
+const later = new Temporal.PlainDateTime(2000, 5, 5);
+const result = later.since(earlier, { smallestUnit: "day", roundingIncrement: 2 });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/round-relative-to-receiver.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/round-relative-to-receiver.js
new file mode 100644
index 0000000000..af8a646eb0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/round-relative-to-receiver.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Values are rounded relative to the receiver
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dt1 = new Temporal.PlainDateTime(2019, 1, 1);
+const dt2 = new Temporal.PlainDateTime(2020, 7, 2);
+
+TemporalHelpers.assertDuration(
+ dt2.since(dt1, { smallestUnit: "years", roundingMode: "halfExpand" }),
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "rounds relative to the receiver (positive case)"
+);
+
+TemporalHelpers.assertDuration(
+ dt1.since(dt2, { smallestUnit: "years", roundingMode: "halfExpand" }),
+ -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "rounds relative to the receiver (negative case)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/rounding-zero-year-month-week-length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/rounding-zero-year-month-week-length.js
new file mode 100644
index 0000000000..48be068e79
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/rounding-zero-year-month-week-length.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: >
+ A malicious calendar resulting in a year, month, or week length of zero is
+ handled correctly
+info: |
+ RoundDuration
+ 10.z. If _oneYearDays_ = 0, throw a *RangeError* exception.
+ ...
+ 11.z. If _oneMonthDays_ = 0, throw a *RangeError* exception.
+ ...
+ 12.s. If _oneWeekDays_ = 0, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const cal = new class extends Temporal.Calendar {
+ dateAdd(date, duration, options) {
+ // Called several times, last call sets oneYear/Month/WeekDays to 0
+ return new Temporal.PlainDate(1970, 1, 1);
+ }
+}("iso8601");
+
+const dt1 = new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, 0, 0, cal);
+const dt2 = new Temporal.PlainDateTime(1971, 1, 1, 0, 0, 0, 0, 0, 1, cal);
+
+assert.throws(RangeError, () => dt1.since(dt2, { smallestUnit: "years" }), "zero year length handled correctly");
+assert.throws(RangeError, () => dt1.since(dt2, { smallestUnit: "months" }), "zero month length handled correctly");
+assert.throws(RangeError, () => dt1.since(dt2, { smallestUnit: "weeks" }), "zero week length handled correctly");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-basic.js
new file mode 100644
index 0000000000..823ee96147
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-basic.js
@@ -0,0 +1,51 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Round to different smallest increments
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 321);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "hours", roundingIncrement: 3, roundingMode: "halfExpand" }),
+ 0, 0, 0, 973, 3, 0, 0, 0, 0, 0,
+ "rounds to an increment of hours"
+);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes", roundingIncrement: 30, roundingMode: "halfExpand" }),
+ 0, 0, 0, 973, 4, 30, 0, 0, 0,0,
+ "rounds to an increment of minutes"
+);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds", roundingIncrement: 15, roundingMode: "halfExpand" }),
+ 0, 0, 0, 973, 4, 17, 0, 0, 0, 0,
+ "rounds to an increment of seconds"
+);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 10, roundingMode: "halfExpand" }),
+ 0, 0, 0, 973, 4, 17, 4, 860, 0, 0,
+ "rounds to an increment of milliseconds"
+);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 10, roundingMode: "halfExpand" }),
+ 0, 0, 0, 973, 4, 17, 4, 864, 200, 0,
+ "rounds to an increment of microseconds"
+);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 10, roundingMode: "halfExpand" }),
+ 0, 0, 0, 973, 4, 17, 4, 864, 197, 530,
+ "rounds to an increment of nanoseconds"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-cleanly-divides.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-cleanly-divides.js
new file mode 100644
index 0000000000..9268e99cd5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-cleanly-divides.js
@@ -0,0 +1,42 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Rounding argument cleanly divides the relevant smallest unit
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 321);
+
+[1, 2, 3, 4, 6, 8, 12].forEach((roundingIncrement) => {
+ const options = { smallestUnit: "hours", roundingIncrement };
+ assert(
+ later.since(earlier, options) instanceof Temporal.Duration,
+ `valid hour increments divide into 24 (rounding increment = ${roundingIncrement}, smallest unit = hours)`
+ );
+});
+
+["minutes", "seconds"].forEach((smallestUnit) => {
+ [1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30].forEach((roundingIncrement) => {
+ const options = { smallestUnit, roundingIncrement };
+ assert(
+ later.since(earlier, options) instanceof Temporal.Duration,
+ `valid ${smallestUnit} increments divide into 60 (rounding increment = ${roundingIncrement})`
+ );
+ });
+});
+
+["milliseconds", "microseconds", "nanoseconds"].forEach((smallestUnit) => {
+ [1, 2, 4, 5, 8, 10, 20, 25, 40, 50, 100, 125, 200, 250, 500].forEach((roundingIncrement) => {
+ const options = { smallestUnit, roundingIncrement };
+ assert(
+ later.since(earlier, options) instanceof Temporal.Duration,
+ `valid ${smallestUnit} increments divide into 1000 (rounding increment = ${roundingIncrement})`
+ );
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-does-not-divide.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-does-not-divide.js
new file mode 100644
index 0000000000..7bae2ca7cc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-does-not-divide.js
@@ -0,0 +1,48 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Throw if rounding increment does not cleanly divide the relevant unit
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 321);
+
+const badIncrements = {
+ "hours": 11,
+ "minutes": 29,
+ "seconds": 29,
+ "milliseconds": 29,
+ "microseconds": 29,
+ "nanoseconds": 29
+};
+
+Object.entries(badIncrements).forEach(([unit, bad]) => {
+ assert.throws(
+ RangeError,
+ () => later.since(earlier, { smallestUnit: unit, roundingIncrement: bad }),
+ `throws on increments that do not divide evenly into the next highest (unit = ${unit}, increment = ${bad})`
+ );
+});
+
+const fullIncrements = {
+ "hours": 24,
+ "minutes": 60,
+ "seconds": 60,
+ "milliseconds": 1000,
+ "microseconds": 1000,
+ "nanoseconds": 1000
+};
+
+Object.entries(fullIncrements).forEach(([unit, bad]) => {
+ assert.throws(
+ RangeError,
+ () => later.since(earlier, { smallestUnit: unit, roundingIncrement: bad }),
+ `throws on increments that are equal to the next highest (unit = ${unit}, rounding increment = ${bad}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-nan.js
new file mode 100644
index 0000000000..79691a7652
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-nan.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: RangeError thrown when roundingIncrement option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaindatetime.prototype.since step 13:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322);
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-non-integer.js
new file mode 100644
index 0000000000..d2005912fd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-non-integer.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Rounding for roundingIncrement option
+info: |
+ ToTemporalRoundingIncrement ( _normalizedOptions_ )
+
+ 1. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, *"number"*, *undefined*, *1*<sub>𝔽</sub>).
+ 2. If _increment_ is not finite, throw a *RangeError* exception.
+ 3. Let _integerIncrement_ be truncate(ℝ(_increment_)).
+ 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception.
+ 5. Return _integerIncrement_.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 5);
+const result = later.since(earlier, { roundingIncrement: 2.5, roundingMode: "trunc" });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, "roundingIncrement 2.5 truncates to 2");
+const result2 = later.since(earlier, { smallestUnit: "days", roundingIncrement: 1e9 + 0.5, roundingMode: "expand" });
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 1e9, 0, 0, 0, 0, 0, 0, "roundingIncrement 1e9 + 0.5 truncates to 1e9");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-out-of-range.js
new file mode 100644
index 0000000000..0e9d196c8f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-out-of-range.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: RangeError thrown when roundingIncrement option out of range
+info: |
+ ToTemporalRoundingIncrement ( _normalizedOptions_ )
+
+ 1. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, *"number"*, *undefined*, *1*<sub>𝔽</sub>).
+ 2. If _increment_ is not finite, throw a *RangeError* exception.
+ 3. Let _integerIncrement_ be truncate(ℝ(_increment_)).
+ 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception.
+ 5. Return _integerIncrement_.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 5);
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: -1 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: 0 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: 0.9 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: 1e9 + 1 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: Infinity }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-undefined.js
new file mode 100644
index 0000000000..1e480dd764
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-undefined.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Fallback value for roundingIncrement option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaindatetime.prototype.since step 13:
+ 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322);
+
+const explicit = later.since(earlier, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 397, 1, 1, 1, 1, 1, 1, "default roundingIncrement is 1");
+
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 397, 1, 1, 1, 1, 1, 1, "default roundingIncrement is 1");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-wrong-type.js
new file mode 100644
index 0000000000..2602578de8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-wrong-type.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Type conversions for roundingIncrement option
+info: |
+ sec-getoption step 8.a:
+ a. Set _value_ to ? ToNumber(value).
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaindatetime.prototype.since step 13:
+ 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => later.since(earlier, { roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 397, 1, 1, 1, 1, 1, 1, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 397, 1, 1, 1, 1, 1, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-ceil.js
new file mode 100644
index 0000000000..34a5fc36cb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-ceil.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Tests calculations with roundingMode "ceil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [3], [-2]],
+ ["months", [0, 32], [0, -31]],
+ ["weeks", [0, 0, 140], [0, 0, -139]],
+ ["days", [0, 0, 0, 974], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 973, 5], [0, 0, 0, -973, -4]],
+ ["minutes", [0, 0, 0, 973, 4, 18], [0, 0, 0, -973, -4, -17]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 5], [0, 0, 0, -973, -4, -17, -4]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 865], [0, 0, 0, -973, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 198], [0, 0, 0, -973, -4, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "ceil";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-expand.js
new file mode 100644
index 0000000000..91f7fc35b1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-expand.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Tests calculations with roundingMode "expand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 140], [0, 0, -140]],
+ ["days", [0, 0, 0, 974], [0, 0, 0, -974]],
+ ["hours", [0, 0, 0, 973, 5], [0, 0, 0, -973, -5]],
+ ["minutes", [0, 0, 0, 973, 4, 18], [0, 0, 0, -973, -4, -18]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 5], [0, 0, 0, -973, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 865], [0, 0, 0, -973, -4, -17, -4, -865]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 198], [0, 0, 0, -973, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "expand";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-floor.js
new file mode 100644
index 0000000000..df8eb42a2d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-floor.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Tests calculations with roundingMode "floor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [2], [-3]],
+ ["months", [0, 31], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -140]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -974]],
+ ["hours", [0, 0, 0, 973, 4], [0, 0, 0, -973, -5]],
+ ["minutes", [0, 0, 0, 973, 4, 17], [0, 0, 0, -973, -4, -18]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 4], [0, 0, 0, -973, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 864], [0, 0, 0, -973, -4, -17, -4, -865]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197], [0, 0, 0, -973, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "floor";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..c6f21e1cd4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfCeil.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Tests calculations with roundingMode "halfCeil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 973, 4], [0, 0, 0, -973, -4]],
+ ["minutes", [0, 0, 0, 973, 4, 17], [0, 0, 0, -973, -4, -17]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 5], [0, 0, 0, -973, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 864], [0, 0, 0, -973, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 198], [0, 0, 0, -973, -4, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "halfCeil";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfEven.js
new file mode 100644
index 0000000000..16af003b07
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfEven.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Tests calculations with roundingMode "halfEven".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 973, 4], [0, 0, 0, -973, -4]],
+ ["minutes", [0, 0, 0, 973, 4, 17], [0, 0, 0, -973, -4, -17]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 5], [0, 0, 0, -973, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 864], [0, 0, 0, -973, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 198], [0, 0, 0, -973, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "halfEven";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..d9626be1ba
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfExpand.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Tests calculations with roundingMode "halfExpand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 973, 4], [0, 0, 0, -973, -4]],
+ ["minutes", [0, 0, 0, 973, 4, 17], [0, 0, 0, -973, -4, -17]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 5], [0, 0, 0, -973, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 864], [0, 0, 0, -973, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 198], [0, 0, 0, -973, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "halfExpand";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..401b03bf04
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfFloor.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Tests calculations with roundingMode "halfFloor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 973, 4], [0, 0, 0, -973, -4]],
+ ["minutes", [0, 0, 0, 973, 4, 17], [0, 0, 0, -973, -4, -17]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 5], [0, 0, 0, -973, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 864], [0, 0, 0, -973, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197], [0, 0, 0, -973, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "halfFloor";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..cdcb279d87
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfTrunc.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Tests calculations with roundingMode "halfTrunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 973, 4], [0, 0, 0, -973, -4]],
+ ["minutes", [0, 0, 0, 973, 4, 17], [0, 0, 0, -973, -4, -17]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 5], [0, 0, 0, -973, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 864], [0, 0, 0, -973, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197], [0, 0, 0, -973, -4, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "halfTrunc";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfexpand-default-changes.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfexpand-default-changes.js
new file mode 100644
index 0000000000..4a0e3b2bf2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfexpand-default-changes.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: A different default for largest unit will be used if smallest unit is larger than "days"
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 321);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "years", roundingMode: "halfExpand" }),
+ 3, 0, 0, 0, 0, 0, 0, 0, 0,0,
+ "assumes a different default for largestUnit if smallestUnit is larger than days (smallest unit = years)"
+);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "months", roundingMode: "halfExpand" }),
+ 0, 32, 0, 0, 0, 0, 0, 0, 0, 0,
+ "assumes a different default for largestUnit if smallestUnit is larger than days (smallest unit = months)"
+);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "weeks", roundingMode: "halfExpand" }),
+ 0, 0, 139, 0, 0, 0, 0, 0, 0, 0,
+ "assumes a different default for largestUnit if smallestUnit is larger than days (smallest unit = weeks)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..c146e82462
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-invalid-string.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 123, 987, 500);
+for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) {
+ assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "microsecond", roundingMode }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-trunc-is-default.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-trunc-is-default.js
new file mode 100644
index 0000000000..5e6bf589be
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-trunc-is-default.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Truncation (trunc) is the default rounding mode
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 321);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes" }),
+ 0, 0, 0, 973, 4, 17, 0, 0, 0, 0,
+ "trunc is the default (round up)"
+);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds" }),
+ 0, 0, 0, 973, 4, 17, 4, 0, 0, 0,
+ "trunc is the default (round down)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-trunc.js
new file mode 100644
index 0000000000..d9c74ae636
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-trunc.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Tests calculations with roundingMode "trunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [2], [-2]],
+ ["months", [0, 31], [0, -31]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 973, 4], [0, 0, 0, -973, -4]],
+ ["minutes", [0, 0, 0, 973, 4, 17], [0, 0, 0, -973, -4, -17]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 4], [0, 0, 0, -973, -4, -17, -4]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 864], [0, 0, 0, -973, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197], [0, 0, 0, -973, -4, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "trunc";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-undefined.js
new file mode 100644
index 0000000000..3e12c14e9c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-undefined.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 123, 987, 500);
+
+const explicit1 = later.since(earlier, { smallestUnit: "microsecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 0, 0, 0, 1, 1, 1, 1, 123, 987, 0, "default roundingMode is trunc");
+const implicit1 = later.since(earlier, { smallestUnit: "microsecond" });
+TemporalHelpers.assertDuration(implicit1, 0, 0, 0, 1, 1, 1, 1, 123, 987, 0, "default roundingMode is trunc");
+
+const explicit2 = later.since(earlier, { smallestUnit: "millisecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 0, 0, 0, 1, 1, 1, 1, 123, 0, 0, "default roundingMode is trunc");
+const implicit2 = later.since(earlier, { smallestUnit: "millisecond" });
+TemporalHelpers.assertDuration(implicit2, 0, 0, 0, 1, 1, 1, 1, 123, 0, 0, "default roundingMode is trunc");
+
+const explicit3 = later.since(earlier, { smallestUnit: "second", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit3, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, "default roundingMode is trunc");
+const implicit3 = later.since(earlier, { smallestUnit: "second" });
+TemporalHelpers.assertDuration(implicit3, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, "default roundingMode is trunc");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..ecae2f1b35
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-wrong-type.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => later.since(earlier, { smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 1, 1, 1, 1, 123, 987, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..36f1d33e50
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-invalid-string.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 987, 654, 321);
+const badValues = [
+ "era",
+ "eraYear",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => later.since(earlier, { smallestUnit }),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..5a8b6cda7b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-plurals-accepted.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 12, 13, 35, 57, 988, 655, 322);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => later.since(earlier, { smallestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-undefined.js
new file mode 100644
index 0000000000..7f2571caab
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-undefined.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 987, 654, 321);
+
+const explicit = later.since(earlier, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 1, 1, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 1, 1, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..17d4abb27c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-wrong-type.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 987, 654, 321);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => later.since(earlier, { smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 1, 1, 1, 1, 987, 654, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/subseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/subseconds.js
new file mode 100644
index 0000000000..cfd38c4dfd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/subseconds.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Returned granularity may be finer than seconds
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const feb20 = new Temporal.PlainDateTime(2020, 2, 1, 0, 0);
+const feb21 = new Temporal.PlainDateTime(2020, 2, 2, 0, 0, 0, 250, 250, 250);
+
+TemporalHelpers.assertDuration(
+ feb21.since(feb20, { largestUnit: "milliseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, 86400250, 250, 250,
+ "can return subseconds (milliseconds)"
+);
+
+TemporalHelpers.assertDuration(
+ feb21.since(feb20, { largestUnit: "microseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, 0, 86400250250, 250,
+ "can return subseconds (microseconds)"
+);
+
+TemporalHelpers.assertDuration(
+ feb21.since(feb20, { largestUnit: "nanoseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 86400250250250,
+ "can return subseconds (nanoseconds)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/weeks-months-mutually-exclusive.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/weeks-months-mutually-exclusive.js
new file mode 100644
index 0000000000..6919df2c1c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/weeks-months-mutually-exclusive.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Weeks and months are mutually exclusive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+const laterDateTime = dt.add({ days: 42, hours: 3 });
+
+TemporalHelpers.assertDuration(
+ laterDateTime.since(dt, { largestUnit: "weeks" }),
+ 0, 0, 6, 0, 3, 0, 0, 0, 0, 0,
+ "weeks and months are mutually exclusive (prefer weeks)"
+);
+
+TemporalHelpers.assertDuration(
+ laterDateTime.since(dt, { largestUnit: "months" }),
+ 0, 1, 0, 12, 3, 0, 0, 0,0, 0,
+ "weeks and months are mutually exclusive (prefer months)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/year-zero.js
new file mode 100644
index 0000000000..84d2cfd46d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/year-zero.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-12-07",
+ "-000000-12-07T03:24:30",
+ "-000000-12-07T03:24:30+01:00",
+ "-000000-12-07T03:24:30+00:00[UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/ambiguous-date.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/ambiguous-date.js
new file mode 100644
index 0000000000..f23e3f376c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/ambiguous-date.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Ambiguous subtraction is handled according to the overflow option
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const mar31 = new Temporal.PlainDateTime(2020, 3, 31, 15, 0);
+
+TemporalHelpers.assertPlainDateTime(
+ mar31.subtract({ months: 1 }),
+ 2020, 2, "M02", 29, 15, 0, 0, 0, 0, 0,
+ "constrain when ambiguous result (overflow options not supplied)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ mar31.subtract({ months: 1 }, { overflow: "constrain" }),
+ 2020, 2, "M02", 29, 15, 0, 0, 0, 0, 0,
+ "constrain when ambiguous result (overflow options supplied)"
+);
+
+assert.throws(
+ RangeError,
+ () => mar31.subtract({ months: 1 }, { overflow: "reject" }),
+ "throw when ambiguous result with reject"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-duration-max.js
new file mode 100644
index 0000000000..7b43031195
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-duration-max.js
@@ -0,0 +1,58 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Maximum allowed duration
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1970, 1, 1);
+
+const maxCases = [
+ ["P273790Y8M11DT23H59M59.999999999S", "string with max years"],
+ [{ years: 273790, months: 8, days: 11, nanoseconds: 86399999999999 }, "property bag with max years"],
+ ["P3285488M11DT23H59M59.999999999S", "string with max months"],
+ [{ months: 3285488, days: 11, nanoseconds: 86399999999999 }, "property bag with max months"],
+ ["P14285714W2DT23H59M59.999999999S", "string with max weeks"],
+ [{ weeks: 14285714, days: 2, nanoseconds: 86399999999999 }, "property bag with max weeks"],
+ ["P100000000DT23H59M59.999999999S", "string with max days"],
+ [{ days: 100000000, nanoseconds: 86399999999999 }, "property bag with max days"],
+ ["PT2400000023H59M59.999999999S", "string with max hours"],
+ [{ hours: 2400000023, nanoseconds: 3599999999999 }, "property bag with max hours"],
+ ["PT144000001439M59.999999999S", "string with max minutes"],
+ [{ minutes: 144000001439, nanoseconds: 59999999999 }, "property bag with max minutes"],
+ ["PT8640000086399.999999999S", "string with max seconds"],
+ [{ seconds: 8640000086399, nanoseconds: 999999999 }, "property bag with max seconds"],
+];
+
+for (const [arg, descr] of maxCases) {
+ const result = instance.subtract(arg);
+ TemporalHelpers.assertPlainDateTime(result, -271821, 4, "M04", 19, 0, 0, 0, 0, 0, 1, `operation succeeds with ${descr}`);
+}
+
+const minCases = [
+ ["-P273790Y8M12DT23H59M59.999999999S", "string with min years"],
+ [{ years: -273790, months: -8, days: -12, nanoseconds: -86399999999999 }, "property bag with min years"],
+ ["-P3285488M12DT23H59M59.999999999S", "string with min months"],
+ [{ months: -3285488, days: -12, nanoseconds: -86399999999999 }, "property bag with min months"],
+ ["-P14285714W2DT23H59M59.999999999S", "string with min weeks"],
+ [{ weeks: -14285714, days: -2, nanoseconds: -86399999999999 }, "property bag with min weeks"],
+ ["-P100000000DT23H59M59.999999999S", "string with min days"],
+ [{ days: -100000000, nanoseconds: -86399999999999 }, "property bag with min days"],
+ ["-PT2400000023H59M59.999999999S", "string with min hours"],
+ [{ hours: -2400000023, nanoseconds: -3599999999999 }, "property bag with min hours"],
+ ["-PT144000001439M59.999999999S", "string with min minutes"],
+ [{ minutes: -144000001439, nanoseconds: -59999999999 }, "property bag with min minutes"],
+ ["-PT8640000086399.999999999S", "string with min seconds"],
+ [{ seconds: -8640000086399, nanoseconds: -999999999 }, "property bag with min seconds"],
+];
+
+for (const [arg, descr] of minCases) {
+ const result = instance.subtract(arg);
+ TemporalHelpers.assertPlainDateTime(result, 275760, 9, "M09", 13, 23, 59, 59, 999, 999, 999, `operation succeeds with ${descr}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-duration-out-of-range.js
new file mode 100644
index 0000000000..d5067d41f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-duration-out-of-range.js
@@ -0,0 +1,75 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Duration-like argument that is out of range
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1970, 1, 1);
+
+const cases = [
+ // 2^32 = 4294967296
+ ["P4294967296Y", "string with years > max"],
+ [{ years: 4294967296 }, "property bag with years > max"],
+ ["-P4294967296Y", "string with years < min"],
+ [{ years: -4294967296 }, "property bag with years < min"],
+ ["P4294967296M", "string with months > max"],
+ [{ months: 4294967296 }, "property bag with months > max"],
+ ["-P4294967296M", "string with months < min"],
+ [{ months: -4294967296 }, "property bag with months < min"],
+ ["P4294967296W", "string with weeks > max"],
+ [{ weeks: 4294967296 }, "property bag with weeks > max"],
+ ["-P4294967296W", "string with weeks < min"],
+ [{ weeks: -4294967296 }, "property bag with weeks < min"],
+
+ // ceil(max safe integer / 86400) = 104249991375
+ ["P104249991375D", "string with days > max"],
+ [{ days: 104249991375 }, "property bag with days > max"],
+ ["P104249991374DT24H", "string where hours balance into days > max"],
+ [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"],
+ ["-P104249991375D", "string with days < min"],
+ [{ days: -104249991375 }, "property bag with days < min"],
+ ["-P104249991374DT24H", "string where hours balance into days < min"],
+ [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"],
+
+ // ceil(max safe integer / 3600) = 2501999792984
+ ["PT2501999792984H", "string with hours > max"],
+ [{ hours: 2501999792984 }, "property bag with hours > max"],
+ ["PT2501999792983H60M", "string where minutes balance into hours > max"],
+ [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"],
+ ["-PT2501999792984H", "string with hours < min"],
+ [{ hours: -2501999792984 }, "property bag with hours < min"],
+ ["-PT2501999792983H60M", "string where minutes balance into hours < min"],
+ [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"],
+
+ // ceil(max safe integer / 60) = 150119987579017
+ ["PT150119987579017M", "string with minutes > max"],
+ [{ minutes: 150119987579017 }, "property bag with minutes > max"],
+ ["PT150119987579016M60S", "string where seconds balance into minutes > max"],
+ [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"],
+ ["-PT150119987579017M", "string with minutes < min"],
+ [{ minutes: -150119987579017 }, "property bag with minutes < min"],
+ ["-PT150119987579016M60S", "string where seconds balance into minutes < min"],
+ [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"],
+
+ // 2^53 = 9007199254740992
+ ["PT9007199254740992S", "string with seconds > max"],
+ [{ seconds: 9007199254740992 }, "property bag with seconds > max"],
+ [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"],
+ ["-PT9007199254740992S", "string with seconds < min"],
+ [{ seconds: -9007199254740992 }, "property bag with seconds < min"],
+ [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"],
+];
+
+for (const [arg, descr] of cases) {
+ assert.throws(RangeError, () => instance.subtract(arg), `${descr} is out of range`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-duration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-duration.js
new file mode 100644
index 0000000000..bc8df78a45
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-duration.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Duration object arguments are handled
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const jan31 = new Temporal.PlainDateTime(2020, 1, 31, 15, 0);
+
+const subtractWithDuration = jan31.subtract(new Temporal.Duration(0, 1, 0, 0, 0, 1));
+TemporalHelpers.assertPlainDateTime(
+ subtractWithDuration,
+ 2019, 12, "M12", 31, 14, 59, 0, 0, 0, 0,
+ "Duration argument"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-invalid-property.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-invalid-property.js
new file mode 100644
index 0000000000..139dfa66ea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-invalid-property.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: temporalDurationLike object must contain at least one correctly spelled property
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({}),
+ "Throws TypeError if no property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({ nonsense: true }),
+ "Throws TypeError if no recognized property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({ sign: 1 }),
+ "Sign property is not recognized"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-mixed-sign.js
new file mode 100644
index 0000000000..82e4584910
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-mixed-sign.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Positive and negative values in the temporalDurationLike argument are not acceptable
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+
+["constrain", "reject"].forEach((overflow) => {
+ assert.throws(
+ RangeError,
+ () => instance.subtract({ hours: 1, minutes: -30 }, { overflow }),
+ `mixed positive and negative values always throw (overflow = "${overflow}")`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-not-object.js
new file mode 100644
index 0000000000..62a65a3720
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-not-object.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Passing a primitive other than string to subtract() throws
+features: [Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+assert.throws(TypeError, () => instance.subtract(undefined), "undefined");
+assert.throws(TypeError, () => instance.subtract(null), "null");
+assert.throws(TypeError, () => instance.subtract(true), "boolean");
+assert.throws(RangeError, () => instance.subtract(""), "empty string");
+assert.throws(TypeError, () => instance.subtract(Symbol()), "Symbol");
+assert.throws(TypeError, () => instance.subtract(7), "number");
+assert.throws(TypeError, () => instance.subtract(7n), "bigint");
+assert.throws(TypeError, () => instance.subtract([]), "array");
+assert.throws(TypeError, () => instance.subtract(() => {}), "function");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-singular-properties.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-singular-properties.js
new file mode 100644
index 0000000000..c033a487eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-singular-properties.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Singular properties in the property bag are always ignored
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+
+[
+ { year: 1 },
+ { month: 2 },
+ { week: 3 },
+ { day: 4 },
+ { hour: 5 },
+ { minute: 6 },
+ { second: 7 },
+ { millisecond: 8 },
+ { microsecond: 9 },
+ { nanosecond: 10 },
+].forEach((badObject) => {
+ assert.throws(TypeError, () => instance.subtract(badObject),
+ "Throw TypeError if temporalDurationLike is not valid");
+});
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string-fractional-units-rounding-mode.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string-fractional-units-rounding-mode.js
new file mode 100644
index 0000000000..ff4eb22bd4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string-fractional-units-rounding-mode.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Strings with fractional duration units are rounded with the correct rounding mode
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2);
+
+TemporalHelpers.assertPlainDateTime(datetime.subtract("PT1.03125H"), 2000, 5, "M05", 1, 22, 58, 7, 500, 0, 0,
+ "positive fractional units rounded with correct rounding mode");
+TemporalHelpers.assertPlainDateTime(datetime.subtract("-PT1.03125H"), 2000, 5, "M05", 2, 1, 1, 52, 500, 0, 0,
+ "negative fractional units rounded with correct rounding mode");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string-negative-fractional-units.js
new file mode 100644
index 0000000000..d1ce63b571
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string-negative-fractional-units.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+
+const resultHours = instance.subtract("-PT24.567890123H");
+TemporalHelpers.assertPlainDateTime(resultHours, 2000, 5, "M05", 3, 0, 34, 4, 404, 442, 800, "negative fractional hours");
+
+const resultMinutes = instance.subtract("-PT1440.567890123M");
+TemporalHelpers.assertPlainDateTime(resultMinutes, 2000, 5, "M05", 3, 0, 0, 34, 73, 407, 380, "negative fractional minutes");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string.js
new file mode 100644
index 0000000000..53f5e8101a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: A string is parsed into the correct object when passed as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+const result = instance.subtract("P3D");
+TemporalHelpers.assertPlainDateTime(result, 2000, 4, "M04", 29, 0, 34, 56, 987, 654, 321);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/balance-negative-time-units.js
new file mode 100644
index 0000000000..fd8f37b576
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/balance-negative-time-units.js
@@ -0,0 +1,52 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-addtime step 8:
+ 8. Return ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-adddatetime step 1:
+ 1. Let _timeResult_ be ? AddTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
+ sec-temporal.plaindatetime.prototype.subtract step 5:
+ 5. Let _result_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], −_duration_.[[Years]], −_duration_.[[Months]], −_duration_.[[Weeks]], −_duration_.[[Days]], −_duration_.[[Hours]], −_duration_.[[Minutes]], −_duration_.[[Seconds]], −_duration_.[[Milliseconds]], −_duration_.[[Microseconds]], −_duration_.[[Nanoseconds]], _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1996, 5, 2, 1, 1, 1, 1, 1, 1);
+
+const result1 = datetime.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainDateTime(result1, 1996, 5, "M05", 2, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
+
+const result2 = datetime.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainDateTime(result2, 1996, 5, "M05", 2, 1, 1, 1, 0, 999, 1, "microseconds balance");
+
+const result3 = datetime.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainDateTime(result3, 1996, 5, "M05", 2, 1, 1, 0, 999, 1, 1, "milliseconds balance");
+
+const result4 = datetime.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainDateTime(result4, 1996, 5, "M05", 2, 1, 0, 59, 1, 1, 1, "seconds balance");
+
+const result5 = datetime.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainDateTime(result5, 1996, 5, "M05", 2, 0, 59, 1, 1, 1, 1, "minutes balance");
+
+const result6 = datetime.subtract(new Temporal.Duration(0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainDateTime(result6, 1996, 5, "M05", 1, 23, 1, 1, 1, 1, 1, "hours balance");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/branding.js
new file mode 100644
index 0000000000..d2bd3b7bef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const subtract = Temporal.PlainDateTime.prototype.subtract;
+
+assert.sameValue(typeof subtract, "function");
+
+const args = [new Temporal.Duration(5)];
+
+assert.throws(TypeError, () => subtract.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => subtract.apply(null, args), "null");
+assert.throws(TypeError, () => subtract.apply(true, args), "true");
+assert.throws(TypeError, () => subtract.apply("", args), "empty string");
+assert.throws(TypeError, () => subtract.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => subtract.apply(1, args), "1");
+assert.throws(TypeError, () => subtract.apply({}, args), "plain object");
+assert.throws(TypeError, () => subtract.apply(Temporal.PlainDateTime, args), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => subtract.apply(Temporal.PlainDateTime.prototype, args), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..2e67ff726e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateAddOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateAdd");
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateAdd should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.subtract(new Temporal.Duration(1));
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", dateAddOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/builtin.js
new file mode 100644
index 0000000000..2abbf5b33f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: >
+ Tests that Temporal.PlainDateTime.prototype.subtract
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.subtract),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.subtract),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.subtract),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.subtract.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/calendar-dateadd.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/calendar-dateadd.js
new file mode 100644
index 0000000000..7b6ec5cff6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/calendar-dateadd.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: PlainDateTime.prototype.subtract should call dateAdd with the appropriate values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(plainDate, duration, options) {
+ ++calls;
+ TemporalHelpers.assertPlainDate(plainDate, 2020, 3, "M03", 14, "plainDate argument");
+ TemporalHelpers.assertDuration(duration, 0, -10, 0, -1, 0, 0, 0, 0, 0, 0, "duration argument");
+ assert.sameValue(typeof options, "object", "options argument: type");
+ assert.sameValue(Object.getPrototypeOf(options), null, "options argument: prototype");
+ return super.dateAdd(plainDate, duration, options);
+ }
+}
+
+const plainDateTime = new Temporal.PlainDateTime(2020, 3, 14, 12, 34, 56, 987, 654, 321, new CustomCalendar());
+const result = plainDateTime.subtract({ months: 10, hours: 14 });
+TemporalHelpers.assertPlainDateTime(result, 2019, 5, "M05", 13, 22, 34, 56, 987, 654, 321);
+assert.sameValue(calls, 1, "should have called dateAdd");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/hour-overflow.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/hour-overflow.js
new file mode 100644
index 0000000000..6da61f9205
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/hour-overflow.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Testing overflow hours (subtracting hours that push one to the next/previous day)
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const dt = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102);
+const later = new Temporal.PlainDateTime(2020, 5, 31, 23, 12, 38, 271, 986, 102);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.subtract({ hours: 12 }),
+ 2019, 10, "M10", 28, 22, 46, 38, 271, 986, 102,
+ "subtract result"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.add({ hours: -12 }),
+ 2019, 10, "M10", 28, 22, 46, 38, 271, 986, 102,
+ "hour overflow (pushes to previous day)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ later.subtract({ hours: -2 }),
+ 2020, 6, "M06", 1, 1, 12, 38, 271, 986, 102,
+ "subtracting a negative amount of hours is equivalent to adding hours"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..b91a8d9857
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainDateTime.prototype.subtract throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.plaindatetime.prototype.subtract
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/length.js
new file mode 100644
index 0000000000..ccb418cc58
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Temporal.PlainDateTime.prototype.subtract.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.subtract, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/limits.js
new file mode 100644
index 0000000000..a71c40fc40
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/limits.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Checking limits of representable PlainDateTime
+features: [Temporal]
+---*/
+
+const min = new Temporal.PlainDateTime(-271821, 4, 19, 0, 0, 0, 0, 0, 1);
+const max = new Temporal.PlainDateTime(275760, 9, 13, 23, 59, 59, 999, 999, 999);
+
+["reject", "constrain"].forEach((overflow) => {
+ assert.throws(
+ RangeError,
+ () => min.subtract({ nanoseconds: 1 }, { overflow }),
+ `subtracting 1 nanosecond beyond minimum limit (overflow = ${overflow})`
+ );
+ assert.throws(
+ RangeError,
+ () => max.subtract({ nanoseconds: -1 }, { overflow }),
+ `subtracting -1 nanosecond beyond maximum limit (overflow = ${overflow})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/name.js
new file mode 100644
index 0000000000..beaac9ddae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Temporal.PlainDateTime.prototype.subtract.name is "subtract".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.subtract, "name", {
+ value: "subtract",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/negative-duration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/negative-duration.js
new file mode 100644
index 0000000000..c4bd186e02
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/negative-duration.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Negative durations can be supplied
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const jan31 = new Temporal.PlainDateTime(2020, 1, 31, 15, 0);
+
+TemporalHelpers.assertPlainDateTime(
+ jan31.subtract({ minutes: -30 }),
+ 2020, 1, "M01", 31, 15, 30, 0, 0, 0, 0,
+ "negative minutes"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ jan31.subtract({ seconds: -30 }),
+ 2020, 1, "M01", 31, 15, 0, 30, 0, 0, 0,
+ "negative seconds"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..e767d44cb7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainDateTime.prototype.subtract throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.plaindatetime.prototype.subtract
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: -Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/non-integer-throws-rangeerror.js
new file mode 100644
index 0000000000..d9e9765eb1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/non-integer-throws-rangeerror.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.subtract({ [field]: -1.5 }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/not-a-constructor.js
new file mode 100644
index 0000000000..eef1ef6b6f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: >
+ Temporal.PlainDateTime.prototype.subtract does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.subtract();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.subtract), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.subtract)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-empty.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-empty.js
new file mode 100644
index 0000000000..a26714caef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-empty.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const jan31 = new Temporal.PlainDateTime(2020, 1, 31, 15, 0);
+
+TemporalHelpers.assertPlainDateTime(
+ jan31.subtract({ months: 2 }, {}),
+ 2019, 11, "M11", 30, 15, 0, 0, 0, 0, 0,
+ "options may be empty object"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ jan31.subtract({ months: 2 }, () => {}),
+ 2019, 11, "M11", 30, 15, 0, 0, 0, 0, 0,
+ "options may be function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-invalid.js
new file mode 100644
index 0000000000..9fc78fd1aa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-invalid.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Various invalid (wrong type) values for options argument
+features: [Temporal, Symbol]
+---*/
+
+const jan31 = new Temporal.PlainDateTime(2020, 1, 31, 15, 0);
+
+const badOptions = [null, 1, 'hello', true, Symbol('foo'), 1n];
+
+badOptions.forEach((bad) => {
+ assert.throws(
+ TypeError,
+ () => jan31.subtract({ years: 1 }, bad),
+ `invalid options (${typeof bad})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-undefined.js
new file mode 100644
index 0000000000..81f22694a9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-undefined.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 3, 31, 12, 34, 56, 987, 654, 321);
+const duration = { months: 1 };
+
+const explicit = datetime.subtract(duration, undefined);
+assert.sameValue(explicit.month, 2, "default overflow is constrain");
+assert.sameValue(explicit.day, 29, "default overflow is constrain");
+
+const implicit = datetime.subtract(duration);
+assert.sameValue(implicit.month, 2, "default overflow is constrain");
+assert.sameValue(implicit.day, 29, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-wrong-type.js
new file mode 100644
index 0000000000..bea427754a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-wrong-type.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.subtract({ months: 1 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/order-of-operations.js
new file mode 100644
index 0000000000..2dff5bdcf7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/order-of-operations.js
@@ -0,0 +1,127 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Properties on an object passed to subtract() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalDurationRecord
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.months.valueOf",
+ "call fields.months.valueOf",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.weeks.valueOf",
+ "call fields.weeks.valueOf",
+ "get fields.years",
+ "get fields.years.valueOf",
+ "call fields.years.valueOf",
+ // AddDateTime -> AddDate
+ "get this.calendar.dateAdd",
+ "call this.calendar.dateAdd",
+ // inside Calendar.p.dateAdd
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options");
+
+instance.subtract(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+actual.splice(0); // clear
+
+const noCalendarExpected = [
+ // ToTemporalDurationRecord
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.years",
+ "get this.calendar.dateAdd",
+ // AddDateTime -> AddDate
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+
+const noCalendarFields = TemporalHelpers.propertyBagObserver(actual, {
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+instance.subtract(noCalendarFields, options);
+assert.compareArray(actual, noCalendarExpected, "order of operations with no calendar operation");
+
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-invalid-string.js
new file mode 100644
index 0000000000..65a10f333f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-invalid-string.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-adddatetime step 4:
+ 4. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.plaindatetime.prototype.subtract step 5:
+ 5. Let _result_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], −_duration_.[[Years]], −_duration_.[[Months]], −_duration_.[[Weeks]], −_duration_.[[Days]], −_duration_.[[Hours]], −_duration_.[[Minutes]], −_duration_.[[Seconds]], −_duration_.[[Milliseconds]], −_duration_.[[Microseconds]], −_duration_.[[Nanoseconds]], _options_).
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDateTime(2000, 5, 2, 12);
+const duration = new Temporal.Duration(3, 3, 0, 3, 3);
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => date.subtract(duration, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-undefined.js
new file mode 100644
index 0000000000..ddcfaf39fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-undefined.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-adddatetime step 4:
+ 4. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.plaindatetime.prototype.subtract step 5:
+ 5. Let _result_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], −_duration_.[[Years]], −_duration_.[[Months]], −_duration_.[[Weeks]], −_duration_.[[Days]], −_duration_.[[Hours]], −_duration_.[[Minutes]], −_duration_.[[Seconds]], −_duration_.[[Milliseconds]], −_duration_.[[Microseconds]], −_duration_.[[Nanoseconds]], _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 31, 12);
+const duration = new Temporal.Duration(3, 1);
+
+const explicit = datetime.subtract(duration, { overflow: undefined });
+TemporalHelpers.assertPlainDateTime(explicit, 1997, 4, "M04", 30, 12, 0, 0, 0, 0, 0, "default overflow is constrain");
+const implicit = datetime.subtract(duration, {});
+TemporalHelpers.assertPlainDateTime(implicit, 1997, 4, "M04", 30, 12, 0, 0, 0, 0, 0, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-wrong-type.js
new file mode 100644
index 0000000000..debacd548a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-wrong-type.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-adddatetime step 4:
+ 4. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.plaindatetime.prototype.subtract step 5:
+ 5. Let _result_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], −_duration_.[[Years]], −_duration_.[[Months]], −_duration_.[[Weeks]], −_duration_.[[Days]], −_duration_.[[Hours]], −_duration_.[[Minutes]], −_duration_.[[Seconds]], −_duration_.[[Milliseconds]], −_duration_.[[Microseconds]], −_duration_.[[Nanoseconds]], _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12);
+const duration = new Temporal.Duration(3, 3, 0, 3, 3);
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => datetime.subtract(duration, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainDateTime(result, 1997, 1, "M01", 30, 9, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/prop-desc.js
new file mode 100644
index 0000000000..19a42d65fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: The "subtract" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.subtract,
+ "function",
+ "`typeof PlainDateTime.prototype.subtract` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "subtract", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/subclassing-ignored.js
new file mode 100644
index 0000000000..7bc53d362e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/subclassing-ignored.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Objects of a subclass are never created as return values for subtract()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDateTime,
+ [2000, 5, 2, 12, 34, 56, 987, 654, 321],
+ "subtract",
+ [{ nanoseconds: 1 }],
+ (result) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 320),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/basic.js
new file mode 100644
index 0000000000..08c59f7a6f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/basic.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tojson
+description: Basic behavior for toJSON
+features: [Temporal]
+---*/
+
+const tests = [
+ [new Temporal.PlainDateTime(1976, 2, 4, 5, 3, 1), "1976-02-04T05:03:01"],
+ [new Temporal.PlainDateTime(1976, 11, 18, 15, 23), "1976-11-18T15:23:00"],
+ [new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30), "1976-11-18T15:23:30"],
+ [new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 400), "1976-11-18T15:23:30.1234"],
+];
+
+const options = new Proxy({}, {
+ get() { throw new Test262Error("should not get properties off argument") }
+});
+for (const [datetime, expected] of tests) {
+ assert.sameValue(datetime.toJSON(), expected, "toJSON without argument");
+ assert.sameValue(datetime.toJSON(options), expected, "toJSON with argument");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/branding.js
new file mode 100644
index 0000000000..1463d2a4cf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tojson
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toJSON = Temporal.PlainDateTime.prototype.toJSON;
+
+assert.sameValue(typeof toJSON, "function");
+
+assert.throws(TypeError, () => toJSON.call(undefined), "undefined");
+assert.throws(TypeError, () => toJSON.call(null), "null");
+assert.throws(TypeError, () => toJSON.call(true), "true");
+assert.throws(TypeError, () => toJSON.call(""), "empty string");
+assert.throws(TypeError, () => toJSON.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toJSON.call(1), "1");
+assert.throws(TypeError, () => toJSON.call({}), "plain object");
+assert.throws(TypeError, () => toJSON.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => toJSON.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..d9ab62d1cc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tojson
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.toJSON();
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/builtin.js
new file mode 100644
index 0000000000..d28100188d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tojson
+description: >
+ Tests that Temporal.PlainDateTime.prototype.toJSON
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.toJSON),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.toJSON),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.toJSON),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.toJSON.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/length.js
new file mode 100644
index 0000000000..9920ebb9ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tojson
+description: Temporal.PlainDateTime.prototype.toJSON.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toJSON, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/name.js
new file mode 100644
index 0000000000..6ff7b93cce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tojson
+description: Temporal.PlainDateTime.prototype.toJSON.name is "toJSON".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toJSON, "name", {
+ value: "toJSON",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/not-a-constructor.js
new file mode 100644
index 0000000000..ed5ee396d9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tojson
+description: >
+ Temporal.PlainDateTime.prototype.toJSON does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.toJSON();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.toJSON), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.toJSON)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/prop-desc.js
new file mode 100644
index 0000000000..2683d24810
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tojson
+description: The "toJSON" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.toJSON,
+ "function",
+ "`typeof PlainDateTime.prototype.toJSON` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "toJSON", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/year-format.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/year-format.js
new file mode 100644
index 0000000000..5f8f2010e2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/year-format.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tojson
+description: Verify that the year is appropriately formatted as 4 or 6 digits
+features: [Temporal]
+---*/
+
+let instance = new Temporal.PlainDateTime(-100000, 12, 3, 4, 56, 7, 890);
+assert.sameValue(instance.toJSON(), "-100000-12-03T04:56:07.89", "large negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDateTime(-10000, 4, 5, 6, 7, 8, 910);
+assert.sameValue(instance.toJSON(), "-010000-04-05T06:07:08.91", "smallest 5-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDateTime(-9999, 6, 7, 8, 9, 10, 987);
+assert.sameValue(instance.toJSON(), "-009999-06-07T08:09:10.987", "largest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDateTime(-1000, 8, 9, 10, 9, 8, 765);
+assert.sameValue(instance.toJSON(), "-001000-08-09T10:09:08.765", "smallest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDateTime(-999, 10, 9, 8, 7, 6, 543);
+assert.sameValue(instance.toJSON(), "-000999-10-09T08:07:06.543", "largest 3-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDateTime(-1, 8, 7, 6, 54, 32, 100);
+assert.sameValue(instance.toJSON(), "-000001-08-07T06:54:32.1", "year -1 formatted as 6-digit");
+
+instance = new Temporal.PlainDateTime(0, 6, 5, 4, 32, 10, 123);
+assert.sameValue(instance.toJSON(), "0000-06-05T04:32:10.123", "year 0 formatted as 4-digit");
+
+instance = new Temporal.PlainDateTime(1, 4, 3, 21, 0, 12, 345);
+assert.sameValue(instance.toJSON(), "0001-04-03T21:00:12.345", "year 1 formatted as 4-digit");
+
+instance = new Temporal.PlainDateTime(999, 2, 10, 12, 34, 56, 789);
+assert.sameValue(instance.toJSON(), "0999-02-10T12:34:56.789", "largest 3-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainDateTime(1000, 1, 23, 4, 56, 7, 890);
+assert.sameValue(instance.toJSON(), "1000-01-23T04:56:07.89", "smallest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainDateTime(9999, 4, 5, 6, 7, 8, 910);
+assert.sameValue(instance.toJSON(), "9999-04-05T06:07:08.91", "largest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainDateTime(10000, 6, 7, 8, 9, 10, 987);
+assert.sameValue(instance.toJSON(), "+010000-06-07T08:09:10.987", "smallest 5-digit positive year formatted as 6-digit");
+
+instance = new Temporal.PlainDateTime(100000, 8, 9, 10, 9, 8, 765);
+assert.sameValue(instance.toJSON(), "+100000-08-09T10:09:08.765", "large positive year formatted as 6-digit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/branding.js
new file mode 100644
index 0000000000..bca2248179
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tolocalestring
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toLocaleString = Temporal.PlainDateTime.prototype.toLocaleString;
+
+assert.sameValue(typeof toLocaleString, "function");
+
+assert.throws(TypeError, () => toLocaleString.call(undefined), "undefined");
+assert.throws(TypeError, () => toLocaleString.call(null), "null");
+assert.throws(TypeError, () => toLocaleString.call(true), "true");
+assert.throws(TypeError, () => toLocaleString.call(""), "empty string");
+assert.throws(TypeError, () => toLocaleString.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toLocaleString.call(1), "1");
+assert.throws(TypeError, () => toLocaleString.call({}), "plain object");
+assert.throws(TypeError, () => toLocaleString.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => toLocaleString.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..66f7d218c3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tolocalestring
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.toLocaleString();
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/builtin.js
new file mode 100644
index 0000000000..9f6fcf57ec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tolocalestring
+description: >
+ Tests that Temporal.PlainDateTime.prototype.toLocaleString
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.toLocaleString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.toLocaleString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.toLocaleString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.toLocaleString.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/length.js
new file mode 100644
index 0000000000..25b9d265fb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tolocalestring
+description: Temporal.PlainDateTime.prototype.toLocaleString.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toLocaleString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/name.js
new file mode 100644
index 0000000000..51a6a67421
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tolocalestring
+description: Temporal.PlainDateTime.prototype.toLocaleString.name is "toLocaleString".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toLocaleString, "name", {
+ value: "toLocaleString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/not-a-constructor.js
new file mode 100644
index 0000000000..1016478185
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tolocalestring
+description: >
+ Temporal.PlainDateTime.prototype.toLocaleString does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.toLocaleString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.toLocaleString), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.toLocaleString)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/prop-desc.js
new file mode 100644
index 0000000000..09aa7925af
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tolocalestring
+description: The "toLocaleString" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.toLocaleString,
+ "function",
+ "`typeof PlainDateTime.prototype.toLocaleString` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "toLocaleString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/return-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/return-string.js
new file mode 100644
index 0000000000..e7285c35fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/return-string.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Kate Miháliková. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tolocalestring
+description: >
+ toLocaleString return a string.
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+assert.sameValue(typeof datetime.toLocaleString("en", { dateStyle: "short" }), "string");
+assert.sameValue(typeof datetime.toLocaleString("en", { timeStyle: "short" }), "string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/branding.js
new file mode 100644
index 0000000000..a0f01d67d7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplaindate
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toPlainDate = Temporal.PlainDateTime.prototype.toPlainDate;
+
+assert.sameValue(typeof toPlainDate, "function");
+
+assert.throws(TypeError, () => toPlainDate.call(undefined), "undefined");
+assert.throws(TypeError, () => toPlainDate.call(null), "null");
+assert.throws(TypeError, () => toPlainDate.call(true), "true");
+assert.throws(TypeError, () => toPlainDate.call(""), "empty string");
+assert.throws(TypeError, () => toPlainDate.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toPlainDate.call(1), "1");
+assert.throws(TypeError, () => toPlainDate.call({}), "plain object");
+assert.throws(TypeError, () => toPlainDate.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => toPlainDate.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/builtin.js
new file mode 100644
index 0000000000..a76099be6f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplaindate
+description: >
+ Tests that Temporal.PlainDateTime.prototype.toPlainDate
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.toPlainDate),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.toPlainDate),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.toPlainDate),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.toPlainDate.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/length.js
new file mode 100644
index 0000000000..901088a6a1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplaindate
+description: Temporal.PlainDateTime.prototype.toPlainDate.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toPlainDate, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/limits.js
new file mode 100644
index 0000000000..aece7cceb8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/limits.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplaindate
+description: toPlainDate works at the edges of the supported range
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const min = Temporal.PlainDateTime.from('-271821-04-19T00:00:00.000000001');
+TemporalHelpers.assertPlainDate(min.toPlainDate(),
+ -271821, 4, "M04", 19, "min");
+
+const max = Temporal.PlainDateTime.from('+275760-09-13T23:59:59.999999999');
+TemporalHelpers.assertPlainDate(max.toPlainDate(),
+ 275760, 9, "M09", 13, "max");
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/name.js
new file mode 100644
index 0000000000..c63f347aa7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplaindate
+description: Temporal.PlainDateTime.prototype.toPlainDate.name is "toPlainDate".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toPlainDate, "name", {
+ value: "toPlainDate",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/not-a-constructor.js
new file mode 100644
index 0000000000..3f3871e91b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplaindate
+description: >
+ Temporal.PlainDateTime.prototype.toPlainDate does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.toPlainDate();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.toPlainDate), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.toPlainDate)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/prop-desc.js
new file mode 100644
index 0000000000..a6d5f85d15
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplaindate
+description: The "toPlainDate" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.toPlainDate,
+ "function",
+ "`typeof PlainDateTime.prototype.toPlainDate` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "toPlainDate", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/branding.js
new file mode 100644
index 0000000000..7ae3fd08d9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainmonthday
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toPlainMonthDay = Temporal.PlainDateTime.prototype.toPlainMonthDay;
+
+assert.sameValue(typeof toPlainMonthDay, "function");
+
+assert.throws(TypeError, () => toPlainMonthDay.call(undefined), "undefined");
+assert.throws(TypeError, () => toPlainMonthDay.call(null), "null");
+assert.throws(TypeError, () => toPlainMonthDay.call(true), "true");
+assert.throws(TypeError, () => toPlainMonthDay.call(""), "empty string");
+assert.throws(TypeError, () => toPlainMonthDay.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toPlainMonthDay.call(1), "1");
+assert.throws(TypeError, () => toPlainMonthDay.call({}), "plain object");
+assert.throws(TypeError, () => toPlainMonthDay.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => toPlainMonthDay.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..7b347a4d71
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainmonthday
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainDateTime(2023, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.toPlainMonthDay();
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..0653113ec6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainmonthday
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const fieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "fields");
+Object.defineProperty(Temporal.Calendar.prototype, "fields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("fields should not be looked up");
+ },
+});
+const monthDayFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "monthDayFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "monthDayFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("monthDayFromFields should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.toPlainMonthDay();
+
+Object.defineProperty(Temporal.Calendar.prototype, "fields", fieldsOriginal);
+Object.defineProperty(Temporal.Calendar.prototype, "monthDayFromFields", monthDayFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/builtin.js
new file mode 100644
index 0000000000..90003f6f26
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainmonthday
+description: >
+ Tests that Temporal.PlainDateTime.prototype.toPlainMonthDay
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.toPlainMonthDay),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.toPlainMonthDay),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.toPlainMonthDay),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.toPlainMonthDay.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-arguments.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-arguments.js
new file mode 100644
index 0000000000..77368791ee
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-arguments.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainmonthday
+description: Correct options value is passed to calendar method
+info: |
+ MonthDayFromFields ( calendar, fields [ , options ] )
+
+ 3. If options is not present, then
+ a. Set options to undefined.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ monthDayFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.sameValue(args[1], undefined, "args[1]");
+ return super.monthDayFromFields(...args);
+ }
+}
+const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 456, 789, new CustomCalendar());
+const result = plainDateTime.toPlainMonthDay();
+TemporalHelpers.assertPlainMonthDay(result, "M05", 2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-fields-iterable.js
new file mode 100644
index 0000000000..c8e11029a7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-fields-iterable.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainmonthday
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindatetime.prototype.toplainmonthday step 4:
+ 4. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"monthCode"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "monthCode",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+datetime.toPlainMonthDay();
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-fromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..6a7537d9dd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-fromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainmonthday
+description: >
+ Calendar.monthDayFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+instance.toPlainMonthDay();
+assert.sameValue(calendar.monthDayFromFieldsCallCount, 1, "monthDayFromFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-monthdayfromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-monthdayfromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..d464b463c3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-monthdayfromfields-called-with-options-undefined.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainmonthday
+description: >
+ Calendar.monthDayFromFields method is called with undefined as the options
+ value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+instance.toPlainMonthDay();
+assert.sameValue(calendar.monthDayFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..24fa34c241
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainmonthday
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const datetime = new Temporal.PlainDateTime(2023, 5, 1, 0, 0, 0, 0, 0, 0, calendar);
+
+assert.throws(RangeError, () => datetime.toPlainMonthDay());
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..f0c50f3229
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/duplicate-calendar-fields.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainmonthday
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['monthCode'], ['day']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const datetime = new Temporal.PlainDateTime(2023, 5, 1, 0, 0, 0, 0, 0, 0, calendar);
+
+ assert.throws(RangeError, () => datetime.toPlainMonthDay());
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/length.js
new file mode 100644
index 0000000000..923bb0098e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainmonthday
+description: Temporal.PlainDateTime.prototype.toPlainMonthDay.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toPlainMonthDay, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/name.js
new file mode 100644
index 0000000000..e4451e029a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainmonthday
+description: Temporal.PlainDateTime.prototype.toPlainMonthDay.name is "toPlainMonthDay".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toPlainMonthDay, "name", {
+ value: "toPlainMonthDay",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/not-a-constructor.js
new file mode 100644
index 0000000000..7a8546b6ac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainmonthday
+description: >
+ Temporal.PlainDateTime.prototype.toPlainMonthDay does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.toPlainMonthDay();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.toPlainMonthDay), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.toPlainMonthDay)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/prop-desc.js
new file mode 100644
index 0000000000..e240362c1e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainmonthday
+description: The "toPlainMonthDay" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.toPlainMonthDay,
+ "function",
+ "`typeof PlainDateTime.prototype.toPlainMonthDay` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "toPlainMonthDay", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..482bbf5603
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainmonthday
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const datetime = new Temporal.PlainDateTime(2023, 5, 1, 0, 0, 0, 0, 0, 0, calendar);
+
+assert.throws(RangeError, () => datetime.toPlainMonthDay());
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/basic.js
new file mode 100644
index 0000000000..319fc1ec88
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/basic.js
@@ -0,0 +1,15 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplaintime
+description: Basic usage
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDateTime = Temporal.PlainDateTime.from("2020-02-12T11:42:56.987654321+01:00[Europe/Amsterdam]");
+TemporalHelpers.assertPlainTime(plainDateTime.toPlainTime(), 11, 42, 56, 987, 654, 321);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/branding.js
new file mode 100644
index 0000000000..40602b80ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplaintime
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toPlainTime = Temporal.PlainDateTime.prototype.toPlainTime;
+
+assert.sameValue(typeof toPlainTime, "function");
+
+assert.throws(TypeError, () => toPlainTime.call(undefined), "undefined");
+assert.throws(TypeError, () => toPlainTime.call(null), "null");
+assert.throws(TypeError, () => toPlainTime.call(true), "true");
+assert.throws(TypeError, () => toPlainTime.call(""), "empty string");
+assert.throws(TypeError, () => toPlainTime.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toPlainTime.call(1), "1");
+assert.throws(TypeError, () => toPlainTime.call({}), "plain object");
+assert.throws(TypeError, () => toPlainTime.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => toPlainTime.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/builtin.js
new file mode 100644
index 0000000000..86434ef219
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplaintime
+description: >
+ Tests that Temporal.PlainDateTime.prototype.toPlainTime
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.toPlainTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.toPlainTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.toPlainTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.toPlainTime.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/length.js
new file mode 100644
index 0000000000..46609cbece
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplaintime
+description: Temporal.PlainDateTime.prototype.toPlainTime.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toPlainTime, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/name.js
new file mode 100644
index 0000000000..690ec0d448
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplaintime
+description: Temporal.PlainDateTime.prototype.toPlainTime.name is "toPlainTime".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toPlainTime, "name", {
+ value: "toPlainTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/not-a-constructor.js
new file mode 100644
index 0000000000..6f82f65253
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplaintime
+description: >
+ Temporal.PlainDateTime.prototype.toPlainTime does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.toPlainTime();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.toPlainTime), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.toPlainTime)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/prop-desc.js
new file mode 100644
index 0000000000..33c7fe422b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplaintime
+description: The "toPlainTime" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.toPlainTime,
+ "function",
+ "`typeof PlainDateTime.prototype.toPlainTime` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "toPlainTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/branding.js
new file mode 100644
index 0000000000..ebd23a5016
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainyearmonth
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toPlainYearMonth = Temporal.PlainDateTime.prototype.toPlainYearMonth;
+
+assert.sameValue(typeof toPlainYearMonth, "function");
+
+assert.throws(TypeError, () => toPlainYearMonth.call(undefined), "undefined");
+assert.throws(TypeError, () => toPlainYearMonth.call(null), "null");
+assert.throws(TypeError, () => toPlainYearMonth.call(true), "true");
+assert.throws(TypeError, () => toPlainYearMonth.call(""), "empty string");
+assert.throws(TypeError, () => toPlainYearMonth.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toPlainYearMonth.call(1), "1");
+assert.throws(TypeError, () => toPlainYearMonth.call({}), "plain object");
+assert.throws(TypeError, () => toPlainYearMonth.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => toPlainYearMonth.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..d5a326f4d7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainyearmonth
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainDateTime(2023, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.toPlainYearMonth();
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..bfcd5570e6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainyearmonth
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const fieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "fields");
+Object.defineProperty(Temporal.Calendar.prototype, "fields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("fields should not be looked up");
+ },
+});
+const yearMonthFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "yearMonthFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "yearMonthFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("yearMonthFromFields should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.toPlainYearMonth();
+
+Object.defineProperty(Temporal.Calendar.prototype, "fields", fieldsOriginal);
+Object.defineProperty(Temporal.Calendar.prototype, "yearMonthFromFields", yearMonthFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/builtin.js
new file mode 100644
index 0000000000..6728798787
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainyearmonth
+description: >
+ Tests that Temporal.PlainDateTime.prototype.toPlainYearMonth
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.toPlainYearMonth),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.toPlainYearMonth),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.toPlainYearMonth),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.toPlainYearMonth.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-arguments.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-arguments.js
new file mode 100644
index 0000000000..fb4b716c49
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-arguments.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainyearmonth
+description: Correct options value is passed to calendar method
+info: |
+ YearMonthFromFields ( calendar, fields [ , options ] )
+
+ 3. If options is not present, then
+ a. Set options to undefined.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ yearMonthFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.sameValue(args[1], undefined, "args[1]");
+ return super.yearMonthFromFields(...args);
+ }
+}
+const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 456, 789, new CustomCalendar());
+const result = plainDateTime.toPlainYearMonth();
+TemporalHelpers.assertPlainYearMonth(result, 2000, 5, "M05");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-fields-iterable.js
new file mode 100644
index 0000000000..d799f96e4b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-fields-iterable.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainyearmonth
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindatetime.prototype.toplainyearmonth step 4:
+ 4. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+datetime.toPlainYearMonth();
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-fromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..769b532980
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-fromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainyearmonth
+description: >
+ Calendar.yearMonthFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+instance.toPlainYearMonth();
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-yearmonthfromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-yearmonthfromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..a51139bca0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-yearmonthfromfields-called-with-options-undefined.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainyearmonth
+description: >
+ Calendar.yearMonthFromFields method is called with undefined as the options
+ value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+instance.toPlainYearMonth();
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..ae3cdb87c0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainyearmonth
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const datetime = new Temporal.PlainDateTime(2023, 5, 1, 0, 0, 0, 0, 0, 0, calendar);
+
+assert.throws(RangeError, () => datetime.toPlainYearMonth());
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..55446f83eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/duplicate-calendar-fields.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainyearmonth
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const datetime = new Temporal.PlainDateTime(2023, 5, 1, 0, 0, 0, 0, 0, 0, calendar);
+
+ assert.throws(RangeError, () => datetime.toPlainYearMonth());
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/length.js
new file mode 100644
index 0000000000..1ae6d9efd2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainyearmonth
+description: Temporal.PlainDateTime.prototype.toPlainYearMonth.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toPlainYearMonth, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/name.js
new file mode 100644
index 0000000000..b2365b219d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainyearmonth
+description: Temporal.PlainDateTime.prototype.toPlainYearMonth.name is "toPlainYearMonth".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toPlainYearMonth, "name", {
+ value: "toPlainYearMonth",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/not-a-constructor.js
new file mode 100644
index 0000000000..d6bb102ce7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainyearmonth
+description: >
+ Temporal.PlainDateTime.prototype.toPlainYearMonth does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.toPlainYearMonth();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.toPlainYearMonth), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.toPlainYearMonth)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/prop-desc.js
new file mode 100644
index 0000000000..955de19a0b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainyearmonth
+description: The "toPlainYearMonth" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.toPlainYearMonth,
+ "function",
+ "`typeof PlainDateTime.prototype.toPlainYearMonth` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "toPlainYearMonth", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..fb38edd6f2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainyearmonth
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const datetime = new Temporal.PlainDateTime(2023, 5, 1, 0, 0, 0, 0, 0, 0, calendar);
+
+assert.throws(RangeError, () => datetime.toPlainYearMonth());
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/basic.js
new file mode 100644
index 0000000000..274774ef8f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/basic.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Checking the string form of an explicitly constructed instance with all arguments
+features: [Temporal]
+---*/
+
+const calendar = Temporal.Calendar.from("iso8601");
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar);
+
+assert.sameValue(datetime.toString(), "1976-11-18T15:23:30.123456789", "check string value");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/branding.js
new file mode 100644
index 0000000000..2758458150
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toString = Temporal.PlainDateTime.prototype.toString;
+
+assert.sameValue(typeof toString, "function");
+
+assert.throws(TypeError, () => toString.call(undefined), "undefined");
+assert.throws(TypeError, () => toString.call(null), "null");
+assert.throws(TypeError, () => toString.call(true), "true");
+assert.throws(TypeError, () => toString.call(""), "empty string");
+assert.throws(TypeError, () => toString.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toString.call(1), "1");
+assert.throws(TypeError, () => toString.call({}), "plain object");
+assert.throws(TypeError, () => toString.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => toString.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..a24cc7c036
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.toString();
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/builtin.js
new file mode 100644
index 0000000000..d6db5a1527
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: >
+ Tests that Temporal.PlainDateTime.prototype.toString
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.toString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.toString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.toString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.toString.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendar-tostring.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendar-tostring.js
new file mode 100644
index 0000000000..b64c3599cb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendar-tostring.js
@@ -0,0 +1,56 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.protoype.tostring
+description: Number of observable 'toString' calls on the calendar for each value of calendarName
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let calls;
+const customCalendar = {
+ get id() {
+ ++calls;
+ return "custom";
+ },
+ toString() {
+ TemporalHelpers.assertUnreachable('toString should not be called');
+ },
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const date = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, customCalendar);
+[
+ ["always", "2000-05-02T12:34:56.987654321[u-ca=custom]", 1],
+ ["auto", "2000-05-02T12:34:56.987654321[u-ca=custom]", 1],
+ ["critical", "2000-05-02T12:34:56.987654321[!u-ca=custom]", 1],
+ ["never", "2000-05-02T12:34:56.987654321", 0],
+ [undefined, "2000-05-02T12:34:56.987654321[u-ca=custom]", 1],
+].forEach(([calendarName, expectedResult, expectedCalls]) => {
+ calls = 0;
+ const result = date.toString({ calendarName });
+ assert.sameValue(result, expectedResult, `id for calendarName = ${calendarName}`);
+ assert.sameValue(calls, expectedCalls, `calls to id getter for calendarName = ${calendarName}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-always.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-always.js
new file mode 100644
index 0000000000..6c251461e3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-always.js
@@ -0,0 +1,48 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: If calendarName is "always", the calendar ID should be included.
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "1976-11-18T15:23:00[u-ca=iso8601]", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "1976-11-18T15:23:00[u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "1976-11-18T15:23:00[u-ca=iso8601]", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "1976-11-18T15:23:00[u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "1976-11-18T15:23:00[u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const date = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 0, 0, 0, 0, ...args);
+ const result = date.toString({ calendarName: "always" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = always`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-auto.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-auto.js
new file mode 100644
index 0000000000..7b5ebf5e4c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-auto.js
@@ -0,0 +1,48 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: If calendarName is "auto", "iso8601" should be omitted.
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "1976-11-18T15:23:00", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "1976-11-18T15:23:00[u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "1976-11-18T15:23:00", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "1976-11-18T15:23:00[u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "1976-11-18T15:23:00[u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const date = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 0, 0, 0, 0, ...args);
+ const result = date.toString({ calendarName: "auto" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = auto`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-critical.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-critical.js
new file mode 100644
index 0000000000..b1846de272
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-critical.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: >
+ If calendarName is "calendar", the calendar ID should be included and prefixed
+ with "!".
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "1976-11-18T15:23:00[!u-ca=iso8601]", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "1976-11-18T15:23:00[!u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "1976-11-18T15:23:00[!u-ca=iso8601]", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "1976-11-18T15:23:00[!u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "1976-11-18T15:23:00[!u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const date = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 0, 0, 0, 0, ...args);
+ const result = date.toString({ calendarName: "critical" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = critical`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-invalid-string.js
new file mode 100644
index 0000000000..bdd5ed36cb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-invalid-string.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.protoype.tostring
+description: RangeError thrown when calendarName option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « *"string"* », « *"auto"*, *"always"*, *"never"*, *"critical"* », *"auto"*).
+ sec-temporal.plaindatetime.protoype.tostring step 6:
+ 6. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const invalidValues = ["ALWAYS", "sometimes", "other string", "auto\0"];
+
+for (const calendarName of invalidValues) {
+ assert.throws(
+ RangeError,
+ () => datetime.toString({ calendarName }),
+ `${calendarName} is an invalid value for calendarName option`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-never.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-never.js
new file mode 100644
index 0000000000..8c5215fa0b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-never.js
@@ -0,0 +1,48 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: If calendarName is "never", the calendar ID should be omitted.
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "1976-11-18T15:23:00", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "1976-11-18T15:23:00", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "1976-11-18T15:23:00", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "1976-11-18T15:23:00", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "1976-11-18T15:23:00", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const date = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 0, 0, 0, 0, ...args);
+ const result = date.toString({ calendarName: "never" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = never`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-undefined.js
new file mode 100644
index 0000000000..a888d1c815
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-undefined.js
@@ -0,0 +1,56 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.protoype.tostring
+description: Fallback value for calendarName option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « *"string"* », « *"auto"*, *"always"*, *"never"*, *"critical"* », *"auto"*).
+ sec-temporal.plaindatetime.protoype.tostring step 6:
+ 6. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "1976-11-18T15:23:00", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "1976-11-18T15:23:00[u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "1976-11-18T15:23:00", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "1976-11-18T15:23:00[u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "1976-11-18T15:23:00[u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 0, 0, 0, 0, ...args);
+ const result = datetime.toString({ calendarName: undefined });
+ assert.sameValue(result, expected, `default calendarName option is auto with ${description} calendar`);
+ // See options-object.js for {} and options-undefined.js for absent options arg
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-wrong-type.js
new file mode 100644
index 0000000000..12b13a24cb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-wrong-type.js
@@ -0,0 +1,49 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.protoype.tostring
+description: Type conversions for calendarName option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « *"string"* », « *"auto"*, *"always"*, *"never"*, *"critical"* », *"auto"*).
+ sec-temporal.plaindatetime.protoype.tostring step 6:
+ 6. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = {
+ id: "custom",
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+
+TemporalHelpers.checkStringOptionWrongType("calendarName", "auto",
+ (calendarName) => datetime.toString({ calendarName }),
+ (result, descr) => assert.sameValue(result, "2000-05-02T12:34:56.987654321[u-ca=custom]", descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-auto.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-auto.js
new file mode 100644
index 0000000000..9bee10c937
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-auto.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: auto value for fractionalSecondDigits option
+features: [Temporal]
+---*/
+
+const tests = [
+ [new Temporal.PlainDateTime(1976, 2, 4, 5, 3, 1), "1976-02-04T05:03:01"],
+ [new Temporal.PlainDateTime(1976, 11, 18, 15, 23), "1976-11-18T15:23:00"],
+ [new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30), "1976-11-18T15:23:30"],
+ [new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 400), "1976-11-18T15:23:30.1234"],
+];
+
+for (const [datetime, expected] of tests) {
+ assert.sameValue(datetime.toString(), expected, "default is to emit seconds and drop trailing zeroes");
+ assert.sameValue(datetime.toString({ fractionalSecondDigits: "auto" }), expected, "auto is the default");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-invalid-string.js
new file mode 100644
index 0000000000..917c2d7a48
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-invalid-string.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option not one of the allowed string values
+info: |
+ sec-getstringornumberoption step 4:
+ 4. If _stringValues_ is not *undefined* and _stringValues_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaindatetime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 650, 0);
+
+for (const fractionalSecondDigits of ["other string", "AUTO", "not-auto", "autos", "auto\0"]) {
+ assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits }),
+ `"${fractionalSecondDigits}" is not a valid value for fractionalSecondDigits`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-nan.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-nan.js
new file mode 100644
index 0000000000..05e1133aa9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-nan.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaindatetime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 650, 0);
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-non-integer.js
new file mode 100644
index 0000000000..38444c4b52
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-non-integer.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Rounding for fractionalSecondDigits option
+info: |
+ sec-getstringornumberoption step 3.b:
+ b. Return floor(ℝ(_value_)).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaindatetime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 650, 0);
+
+let string = datetime.toString({ fractionalSecondDigits: 2.5 });
+assert.sameValue(string, "2000-05-02T12:34:56.98", "fractionalSecondDigits 2.5 floors to 2");
+
+string = datetime.toString({ fractionalSecondDigits: 9.7 });
+assert.sameValue(string, "2000-05-02T12:34:56.987650000", "fractionalSecondDigits 9.7 floors to 9 and is not out of range");
+
+assert.throws(
+ RangeError,
+ () => datetime.toString({ fractionalSecondDigits: -0.6 }),
+ "fractionalSecondDigits -0.6 floors to -1 and is out of range"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-number.js
new file mode 100644
index 0000000000..41eb31d42d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-number.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Number for fractionalSecondDigits option
+features: [Temporal]
+---*/
+
+const fewSeconds = new Temporal.PlainDateTime(1976, 2, 4, 5, 3, 1);
+const zeroSeconds = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+const wholeSeconds = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30);
+const subSeconds = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 400);
+
+assert.sameValue(fewSeconds.toString({ fractionalSecondDigits: 0 }), "1976-02-04T05:03:01",
+ "pads parts with 0");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 0 }), "1976-11-18T15:23:30",
+ "truncates 4 decimal places to 0");
+assert.sameValue(zeroSeconds.toString({ fractionalSecondDigits: 2 }), "1976-11-18T15:23:00.00",
+ "pads zero seconds to 2 decimal places");
+assert.sameValue(wholeSeconds.toString({ fractionalSecondDigits: 2 }), "1976-11-18T15:23:30.00",
+ "pads whole seconds to 2 decimal places");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 2 }), "1976-11-18T15:23:30.12",
+ "truncates 4 decimal places to 2");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 3 }), "1976-11-18T15:23:30.123",
+ "truncates 4 decimal places to 3");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 6 }), "1976-11-18T15:23:30.123400",
+ "pads 4 decimal places to 6");
+assert.sameValue(zeroSeconds.toString({ fractionalSecondDigits: 7 }), "1976-11-18T15:23:00.0000000",
+ "pads zero seconds to 7 decimal places");
+assert.sameValue(wholeSeconds.toString({ fractionalSecondDigits: 7 }), "1976-11-18T15:23:30.0000000",
+ "pads whole seconds to 7 decimal places");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 7 }), "1976-11-18T15:23:30.1234000",
+ "pads 4 decimal places to 7");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 9 }), "1976-11-18T15:23:30.123400000",
+ "pads 4 decimal places to 9");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-out-of-range.js
new file mode 100644
index 0000000000..945bad9c44
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-out-of-range.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option out of range
+info: |
+ sec-getstringornumberoption step 3.a:
+ a. If _value_ < _minimum_ or _value_ > _maximum_, throw a *RangeError* exception.
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaindatetime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 650, 0);
+
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: -Infinity }),
+ "−∞ is out of range for fractionalSecondDigits");
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: -1 }),
+ "−1 is out of range for fractionalSecondDigits");
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: 10 }),
+ "10 is out of range for fractionalSecondDigits");
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: Infinity }),
+ "∞ is out of range for fractionalSecondDigits");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-undefined.js
new file mode 100644
index 0000000000..a46b0f4e13
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-undefined.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Fallback value for fractionalSecondDigits option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaindatetime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const tests = [
+ [new Temporal.PlainDateTime(1976, 2, 4, 5, 3, 1), "1976-02-04T05:03:01"],
+ [new Temporal.PlainDateTime(1976, 11, 18, 15, 23), "1976-11-18T15:23:00"],
+ [new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30), "1976-11-18T15:23:30"],
+ [new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 400), "1976-11-18T15:23:30.1234"],
+];
+
+for (const [datetime, expected] of tests) {
+ const explicit = datetime.toString({ fractionalSecondDigits: undefined });
+ assert.sameValue(explicit, expected, "default fractionalSecondDigits is auto (property present but undefined)");
+
+ const implicit = datetime.toString({});
+ assert.sameValue(implicit, expected, "default fractionalSecondDigits is auto (property not present)");
+
+ const lambda = datetime.toString(() => {});
+ assert.sameValue(lambda, expected, "default fractionalSecondDigits is auto (property not present, function object)");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-wrong-type.js
new file mode 100644
index 0000000000..f803a9978d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-wrong-type.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Type conversions for fractionalSecondDigits option
+info: |
+ sec-getoption steps 8–9:
+ 8. Else if _type_ is Number, then
+ a. Set _value_ to ? ToNumber(value).
+ b. ...
+ 9. Else,
+ a. Set _value_ to ? ToString(value).
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaindatetime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 650, 0);
+
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: null }),
+ "null is not a number and converts to the string 'null' which is not valid for fractionalSecondDigits");
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: true }),
+ "true is not a number and converts to the string 'true' which is not valid for fractionalSecondDigits");
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: false }),
+ "false is not a number and converts to the string 'false' which is not valid for fractionalSecondDigits");
+assert.throws(TypeError, () => datetime.toString({ fractionalSecondDigits: Symbol() }),
+ "symbols are not numbers and cannot convert to strings");
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: 2n }),
+ "bigints are not numbers and convert to strings which are not valid for fractionalSecondDigits");
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: {} }),
+ "plain objects are not numbers and convert to strings which are not valid for fractionalSecondDigits");
+
+const expected = [
+ "get fractionalSecondDigits.toString",
+ "call fractionalSecondDigits.toString",
+];
+const actual = [];
+const observer = TemporalHelpers.toPrimitiveObserver(actual, "auto", "fractionalSecondDigits");
+const result = datetime.toString({ fractionalSecondDigits: observer });
+assert.sameValue(result, "2000-05-02T12:34:56.98765", "object with toString uses toString return value");
+assert.compareArray(actual, expected, "object with toString calls toString and not valueOf");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/length.js
new file mode 100644
index 0000000000..9b5b4a9165
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Temporal.PlainDateTime.prototype.toString.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/name.js
new file mode 100644
index 0000000000..cd3fe445d4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Temporal.PlainDateTime.prototype.toString.name is "toString".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toString, "name", {
+ value: "toString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/not-a-constructor.js
new file mode 100644
index 0000000000..82930cfe3b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: >
+ Temporal.PlainDateTime.prototype.toString does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.toString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.toString), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.toString)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/options-object.js
new file mode 100644
index 0000000000..d0c5ebe101
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/options-object.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Empty or a function object may be used as options
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+
+const result1 = instance.toString({});
+assert.sameValue(
+ result1, "2000-05-02T00:00:00",
+ "options may be an empty plain object"
+);
+
+const result2 = instance.toString(() => {});
+assert.sameValue(
+ result2, "2000-05-02T00:00:00",
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/options-undefined.js
new file mode 100644
index 0000000000..23248ad28c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/options-undefined.js
@@ -0,0 +1,51 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const calendar = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "custom",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const datetime1 = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 650, 0);
+const datetime2 = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 650, 0, calendar);
+
+[
+ [datetime1, "2000-05-02T12:34:56.98765"],
+ [datetime2, "2000-05-02T12:34:56.98765[u-ca=custom]"],
+].forEach(([datetime, expected]) => {
+ const explicit = datetime.toString(undefined);
+ assert.sameValue(explicit, expected, "default calendarName option is auto, precision is auto, and no rounding");
+
+ const propertyImplicit = datetime.toString({});
+ assert.sameValue(propertyImplicit, expected, "default calendarName option is auto, precision is auto, and no rounding");
+
+ const implicit = datetime.toString();
+ assert.sameValue(implicit, expected, "default calendarName option is auto, precision is auto, and no rounding");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/options-wrong-type.js
new file mode 100644
index 0000000000..b473818edd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/options-wrong-type.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.toString(value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/order-of-operations.js
new file mode 100644
index 0000000000..fa47dcb179
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/order-of-operations.js
@@ -0,0 +1,70 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Properties on objects passed to toString() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get options.calendarName",
+ "get options.calendarName.toString",
+ "call options.calendarName.toString",
+ "get options.fractionalSecondDigits",
+ "get options.fractionalSecondDigits.toString",
+ "call options.fractionalSecondDigits.toString",
+ "get options.roundingMode",
+ "get options.roundingMode.toString",
+ "call options.roundingMode.toString",
+ "get options.smallestUnit",
+ "get options.smallestUnit.toString",
+ "call options.smallestUnit.toString",
+ "get this.calendar.id",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainDateTime(1990, 11, 3, 15, 54, 37, 123, 456, 789, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+instance.toString(
+ TemporalHelpers.propertyBagObserver(actual, {
+ fractionalSecondDigits: "auto",
+ roundingMode: "halfExpand",
+ smallestUnit: "millisecond",
+ calendarName: "auto",
+ }, "options"),
+);
+assert.compareArray(actual, expected, "order of operations");
+actual.splice(0); // clear
+
+// Same as above but without options.smallestUnit.toString
+const expectedForFractionalSecondDigits = [
+ "get options.calendarName",
+ "get options.calendarName.toString",
+ "call options.calendarName.toString",
+ "get options.fractionalSecondDigits",
+ "get options.fractionalSecondDigits.toString",
+ "call options.fractionalSecondDigits.toString",
+ "get options.roundingMode",
+ "get options.roundingMode.toString",
+ "call options.roundingMode.toString",
+ "get options.smallestUnit",
+ "get this.calendar.id",
+];
+
+instance.toString(
+ TemporalHelpers.propertyBagObserver(actual, {
+ fractionalSecondDigits: "auto",
+ roundingMode: "halfExpand",
+ smallestUnit: undefined,
+ calendarName: "auto",
+ }, "options"),
+);
+assert.compareArray(actual, expectedForFractionalSecondDigits, "order of operations with smallestUnit undefined");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/prop-desc.js
new file mode 100644
index 0000000000..8206cc471b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: The "toString" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.toString,
+ "function",
+ "`typeof PlainDateTime.prototype.toString` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "toString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/rounding-cross-midnight.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/rounding-cross-midnight.js
new file mode 100644
index 0000000000..a2c73e27e4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/rounding-cross-midnight.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Rounding can cross midnight
+features: [Temporal]
+---*/
+
+const plainDateTime = new Temporal.PlainDateTime(1999, 12, 31, 23, 59, 59, 999, 999, 999); // one nanosecond before 2000-01-01T00:00:00
+for (const roundingMode of ["ceil", "halfExpand"]) {
+ assert.sameValue(plainDateTime.toString({ fractionalSecondDigits: 8, roundingMode }), "2000-01-01T00:00:00.00000000");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/rounding-direction.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/rounding-direction.js
new file mode 100644
index 0000000000..334db9466f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/rounding-direction.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Rounding down is towards the Big Bang, not the epoch or 1 BCE
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(-99, 12, 15, 12, 0, 0, 500);
+assert.sameValue(
+ instance.toString({ smallestUnit: "second", roundingMode: "floor" }),
+ "-000099-12-15T12:00:00",
+ "Rounding down is towards the Big Bang, not the epoch or 1 BCE"
+);
+assert.sameValue(
+ instance.toString({ smallestUnit: "second", roundingMode: "trunc" }),
+ "-000099-12-15T12:00:00",
+ "Rounding down is towards the Big Bang, not the epoch or 1 BCE (roundingMode trunc)"
+);
+assert.sameValue(
+ instance.toString({ smallestUnit: "second", roundingMode: "ceil" }),
+ "-000099-12-15T12:00:01",
+ "Rounding up is away from the Big Bang, not the epoch or 1 BCE (roundingMode ceil)"
+);
+assert.sameValue(
+ instance.toString({ smallestUnit: "second", roundingMode: "halfExpand" }),
+ "-000099-12-15T12:00:01",
+ "Rounding up is away from the Big Bang, not the epoch or 1 BCE (roundingMode halfExpand)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-ceil.js
new file mode 100644
index 0000000000..4a60e4aaa9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-ceil.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: ceil value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+
+const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "ceil" });
+assert.sameValue(result1, "2000-05-02T12:34:56.123988",
+ "roundingMode is ceil (with 6 digits from smallestUnit)");
+
+const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "ceil" });
+assert.sameValue(result2, "2000-05-02T12:34:56.123988",
+ "roundingMode is ceil (with 6 digits from fractionalSecondDigits)");
+
+const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "ceil" });
+assert.sameValue(result3, "2000-05-02T12:34:56.124",
+ "roundingMode is ceil (with 3 digits from smallestUnit)");
+
+const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "ceil" });
+assert.sameValue(result4, "2000-05-02T12:34:56.124",
+ "roundingMode is ceil (with 3 digits from fractionalSecondDigits)");
+
+const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "ceil" });
+assert.sameValue(result5, "2000-05-02T12:34:57",
+ "roundingMode is ceil (with 0 digits from smallestUnit)");
+
+const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "ceil" });
+assert.sameValue(result6, "2000-05-02T12:34:57",
+ "roundingMode is ceil (with 0 digits from fractionalSecondDigits)");
+
+const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "ceil" });
+assert.sameValue(result7, "2000-05-02T12:35", "roundingMode is ceil (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-expand.js
new file mode 100644
index 0000000000..44ef84dea2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-expand.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: expand value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+
+const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "expand" });
+assert.sameValue(result1, "2000-05-02T12:34:56.123988",
+ "roundingMode is expand (with 6 digits from smallestUnit)");
+
+const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "expand" });
+assert.sameValue(result2, "2000-05-02T12:34:56.123988",
+ "roundingMode is expand (with 6 digits from fractionalSecondDigits)");
+
+const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "expand" });
+assert.sameValue(result3, "2000-05-02T12:34:56.124",
+ "roundingMode is expand (with 3 digits from smallestUnit)");
+
+const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "expand" });
+assert.sameValue(result4, "2000-05-02T12:34:56.124",
+ "roundingMode is expand (with 3 digits from fractionalSecondDigits)");
+
+const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "expand" });
+assert.sameValue(result5, "2000-05-02T12:34:57",
+ "roundingMode is expand (with 0 digits from smallestUnit)");
+
+const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "expand" });
+assert.sameValue(result6, "2000-05-02T12:34:57",
+ "roundingMode is expand (with 0 digits from fractionalSecondDigits)");
+
+const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "expand" });
+assert.sameValue(result7, "2000-05-02T12:35", "roundingMode is expand (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-floor.js
new file mode 100644
index 0000000000..1505b99ac5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-floor.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: floor value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+
+const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "floor" });
+assert.sameValue(result1, "2000-05-02T12:34:56.123987",
+ "roundingMode is floor (with 6 digits from smallestUnit)");
+
+const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "floor" });
+assert.sameValue(result2, "2000-05-02T12:34:56.123987",
+ "roundingMode is floor (with 6 digits from fractionalSecondDigits)");
+
+const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "floor" });
+assert.sameValue(result3, "2000-05-02T12:34:56.123",
+ "roundingMode is floor (with 3 digits from smallestUnit)");
+
+const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "floor" });
+assert.sameValue(result4, "2000-05-02T12:34:56.123",
+ "roundingMode is floor (with 3 digits from fractionalSecondDigits)");
+
+const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "floor" });
+assert.sameValue(result5, "2000-05-02T12:34:56",
+ "roundingMode is floor (with 0 digits from smallestUnit)");
+
+const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "floor" });
+assert.sameValue(result6, "2000-05-02T12:34:56",
+ "roundingMode is floor (with 0 digits from fractionalSecondDigits)");
+
+const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "floor" });
+assert.sameValue(result7, "2000-05-02T12:34", "roundingMode is floor (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..4db75c3bc6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfCeil.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: halfCeil value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+
+const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "halfCeil" });
+assert.sameValue(result1, "2000-05-02T12:34:56.123988",
+ "roundingMode is halfCeil (with 6 digits from smallestUnit)");
+
+const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "halfCeil" });
+assert.sameValue(result2, "2000-05-02T12:34:56.123988",
+ "roundingMode is halfCeil (with 6 digits from fractionalSecondDigits)");
+
+const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "halfCeil" });
+assert.sameValue(result3, "2000-05-02T12:34:56.124",
+ "roundingMode is halfCeil (with 3 digits from smallestUnit)");
+
+const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "halfCeil" });
+assert.sameValue(result4, "2000-05-02T12:34:56.124",
+ "roundingMode is halfCeil (with 3 digits from fractionalSecondDigits)");
+
+const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "halfCeil" });
+assert.sameValue(result5, "2000-05-02T12:34:56",
+ "roundingMode is halfCeil (with 0 digits from smallestUnit)");
+
+const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "halfCeil" });
+assert.sameValue(result6, "2000-05-02T12:34:56",
+ "roundingMode is halfCeil (with 0 digits from fractionalSecondDigits)");
+
+const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "halfCeil" });
+assert.sameValue(result7, "2000-05-02T12:35", "roundingMode is halfCeil (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfEven.js
new file mode 100644
index 0000000000..84d09a9bae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfEven.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: halfEven value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+
+const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "halfEven" });
+assert.sameValue(result1, "2000-05-02T12:34:56.123988",
+ "roundingMode is halfEven (with 6 digits from smallestUnit)");
+
+const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "halfEven" });
+assert.sameValue(result2, "2000-05-02T12:34:56.123988",
+ "roundingMode is halfEven (with 6 digits from fractionalSecondDigits)");
+
+const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "halfEven" });
+assert.sameValue(result3, "2000-05-02T12:34:56.124",
+ "roundingMode is halfEven (with 3 digits from smallestUnit)");
+
+const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "halfEven" });
+assert.sameValue(result4, "2000-05-02T12:34:56.124",
+ "roundingMode is halfEven (with 3 digits from fractionalSecondDigits)");
+
+const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "halfEven" });
+assert.sameValue(result5, "2000-05-02T12:34:56",
+ "roundingMode is halfEven (with 0 digits from smallestUnit)");
+
+const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "halfEven" });
+assert.sameValue(result6, "2000-05-02T12:34:56",
+ "roundingMode is halfEven (with 0 digits from fractionalSecondDigits)");
+
+const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "halfEven" });
+assert.sameValue(result7, "2000-05-02T12:35", "roundingMode is halfEven (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..4ef2194248
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfExpand.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: halfExpand value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+
+const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "halfExpand" });
+assert.sameValue(result1, "2000-05-02T12:34:56.123988",
+ "roundingMode is halfExpand (with 6 digits from smallestUnit)");
+
+const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "halfExpand" });
+assert.sameValue(result2, "2000-05-02T12:34:56.123988",
+ "roundingMode is halfExpand (with 6 digits from fractionalSecondDigits)");
+
+const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "halfExpand" });
+assert.sameValue(result3, "2000-05-02T12:34:56.124",
+ "roundingMode is halfExpand (with 3 digits from smallestUnit)");
+
+const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "halfExpand" });
+assert.sameValue(result4, "2000-05-02T12:34:56.124",
+ "roundingMode is halfExpand (with 3 digits from fractionalSecondDigits)");
+
+const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "halfExpand" });
+assert.sameValue(result5, "2000-05-02T12:34:56",
+ "roundingMode is halfExpand (with 0 digits from smallestUnit)");
+
+const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "halfExpand" });
+assert.sameValue(result6, "2000-05-02T12:34:56",
+ "roundingMode is halfExpand (with 0 digits from fractionalSecondDigits)");
+
+const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "halfExpand" });
+assert.sameValue(result7, "2000-05-02T12:35", "roundingMode is halfExpand (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..eb10bd3589
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfFloor.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: halfFloor value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+
+const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "halfFloor" });
+assert.sameValue(result1, "2000-05-02T12:34:56.123987",
+ "roundingMode is halfFloor (with 6 digits from smallestUnit)");
+
+const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "halfFloor" });
+assert.sameValue(result2, "2000-05-02T12:34:56.123987",
+ "roundingMode is halfFloor (with 6 digits from fractionalSecondDigits)");
+
+const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "halfFloor" });
+assert.sameValue(result3, "2000-05-02T12:34:56.124",
+ "roundingMode is halfFloor (with 3 digits from smallestUnit)");
+
+const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "halfFloor" });
+assert.sameValue(result4, "2000-05-02T12:34:56.124",
+ "roundingMode is halfFloor (with 3 digits from fractionalSecondDigits)");
+
+const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "halfFloor" });
+assert.sameValue(result5, "2000-05-02T12:34:56",
+ "roundingMode is halfFloor (with 0 digits from smallestUnit)");
+
+const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "halfFloor" });
+assert.sameValue(result6, "2000-05-02T12:34:56",
+ "roundingMode is halfFloor (with 0 digits from fractionalSecondDigits)");
+
+const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "halfFloor" });
+assert.sameValue(result7, "2000-05-02T12:35", "roundingMode is halfFloor (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..ecad87495c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfTrunc.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: halfTrunc value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+
+const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "halfTrunc" });
+assert.sameValue(result1, "2000-05-02T12:34:56.123987",
+ "roundingMode is halfTrunc (with 6 digits from smallestUnit)");
+
+const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "halfTrunc" });
+assert.sameValue(result2, "2000-05-02T12:34:56.123987",
+ "roundingMode is halfTrunc (with 6 digits from fractionalSecondDigits)");
+
+const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "halfTrunc" });
+assert.sameValue(result3, "2000-05-02T12:34:56.124",
+ "roundingMode is halfTrunc (with 3 digits from smallestUnit)");
+
+const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "halfTrunc" });
+assert.sameValue(result4, "2000-05-02T12:34:56.124",
+ "roundingMode is halfTrunc (with 3 digits from fractionalSecondDigits)");
+
+const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "halfTrunc" });
+assert.sameValue(result5, "2000-05-02T12:34:56",
+ "roundingMode is halfTrunc (with 0 digits from smallestUnit)");
+
+const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "halfTrunc" });
+assert.sameValue(result6, "2000-05-02T12:34:56",
+ "roundingMode is halfTrunc (with 0 digits from fractionalSecondDigits)");
+
+const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "halfTrunc" });
+assert.sameValue(result7, "2000-05-02T12:35", "roundingMode is halfTrunc (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..f9e6668145
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-invalid-string.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) {
+ assert.throws(RangeError, () => datetime.toString({ smallestUnit: "microsecond", roundingMode }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-trunc.js
new file mode 100644
index 0000000000..12080ca5cb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-trunc.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: trunc value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+
+const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "trunc" });
+assert.sameValue(result1, "2000-05-02T12:34:56.123987",
+ "roundingMode is trunc (with 6 digits from smallestUnit)");
+
+const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "trunc" });
+assert.sameValue(result2, "2000-05-02T12:34:56.123987",
+ "roundingMode is trunc (with 6 digits from fractionalSecondDigits)");
+
+const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "trunc" });
+assert.sameValue(result3, "2000-05-02T12:34:56.123",
+ "roundingMode is trunc (with 3 digits from smallestUnit)");
+
+const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "trunc" });
+assert.sameValue(result4, "2000-05-02T12:34:56.123",
+ "roundingMode is trunc (with 3 digits from fractionalSecondDigits)");
+
+const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "trunc" });
+assert.sameValue(result5, "2000-05-02T12:34:56",
+ "roundingMode is trunc (with 0 digits from smallestUnit)");
+
+const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "trunc" });
+assert.sameValue(result6, "2000-05-02T12:34:56",
+ "roundingMode is trunc (with 0 digits from fractionalSecondDigits)");
+
+const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "trunc" });
+assert.sameValue(result7, "2000-05-02T12:34", "roundingMode is trunc (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-undefined.js
new file mode 100644
index 0000000000..9cd034c843
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-undefined.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Fallback value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+
+const explicit1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: undefined });
+assert.sameValue(explicit1, "2000-05-02T12:34:56.123987", "default roundingMode is trunc");
+const implicit1 = datetime.toString({ smallestUnit: "microsecond" });
+assert.sameValue(implicit1, "2000-05-02T12:34:56.123987", "default roundingMode is trunc");
+
+const explicit2 = datetime.toString({ smallestUnit: "millisecond", roundingMode: undefined });
+assert.sameValue(explicit2, "2000-05-02T12:34:56.123", "default roundingMode is trunc");
+const implicit2 = datetime.toString({ smallestUnit: "millisecond" });
+assert.sameValue(implicit2, "2000-05-02T12:34:56.123", "default roundingMode is trunc");
+
+const explicit3 = datetime.toString({ smallestUnit: "second", roundingMode: undefined });
+assert.sameValue(explicit3, "2000-05-02T12:34:56", "default roundingMode is trunc");
+const implicit3 = datetime.toString({ smallestUnit: "second" });
+assert.sameValue(implicit3, "2000-05-02T12:34:56", "default roundingMode is trunc");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..c876d5e65a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-wrong-type.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => datetime.toString({ smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => assert.sameValue(result, "2000-05-02T12:34:56.123987", descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-fractionalseconddigits.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-fractionalseconddigits.js
new file mode 100644
index 0000000000..fae1c3a786
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-fractionalseconddigits.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: fractionalSecondDigits option is not used with smallestUnit present
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 12, 34, 56, 789, 999, 999);
+const tests = [
+ ["minute", "1976-11-18T12:34"],
+ ["second", "1976-11-18T12:34:56"],
+ ["millisecond", "1976-11-18T12:34:56.789"],
+ ["microsecond", "1976-11-18T12:34:56.789999"],
+ ["nanosecond", "1976-11-18T12:34:56.789999999"],
+];
+
+for (const [smallestUnit, expected] of tests) {
+ const string = datetime.toString({
+ smallestUnit,
+ fractionalSecondDigits: 5,
+ });
+ assert.sameValue(string, expected, `smallestUnit: "${smallestUnit}" overrides fractionalSecondDigits`);
+}
+
+assert.throws(RangeError, () => datetime.toString({
+ smallestUnit: "hour",
+ fractionalSecondDigits: 5,
+}), "hour is an invalid smallestUnit but still overrides fractionalSecondDigits");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..81d11967a3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-invalid-string.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+const badValues = [
+ "era",
+ "eraYear",
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => datetime.toString({ smallestUnit }),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..f5eb54beea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-plurals-accepted.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 789, 999, 999);
+const validUnits = [
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => datetime.toString({ smallestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-undefined.js
new file mode 100644
index 0000000000..b10e795149
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-undefined.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Fallback value for smallestUnit option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+
+const explicit1 = datetime.toString({ smallestUnit: undefined, fractionalSecondDigits: 6 });
+assert.sameValue(explicit1, "2000-05-02T12:34:56.123987", "default smallestUnit defers to fractionalSecondDigits");
+const implicit1 = datetime.toString({ fractionalSecondDigits: 6 });
+assert.sameValue(implicit1, "2000-05-02T12:34:56.123987", "default smallestUnit defers to fractionalSecondDigits");
+
+const explicit2 = datetime.toString({ smallestUnit: undefined, fractionalSecondDigits: 3 });
+assert.sameValue(explicit2, "2000-05-02T12:34:56.123", "default smallestUnit defers to fractionalSecondDigits");
+const implicit2 = datetime.toString({ fractionalSecondDigits: 3 });
+assert.sameValue(implicit2, "2000-05-02T12:34:56.123", "default smallestUnit defers to fractionalSecondDigits");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-valid-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-valid-units.js
new file mode 100644
index 0000000000..d766cba089
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-valid-units.js
@@ -0,0 +1,58 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Valid units for the smallestUnit option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 456, 789);
+
+function test(instance, expectations, description) {
+ for (const [smallestUnit, expectedResult] of expectations) {
+ assert.sameValue(instance.toString({ smallestUnit }), expectedResult,
+ `${description} with smallestUnit "${smallestUnit}"`);
+ }
+}
+
+test(
+ datetime,
+ [
+ ["minute", "2000-05-02T12:34"],
+ ["second", "2000-05-02T12:34:56"],
+ ["millisecond", "2000-05-02T12:34:56.123"],
+ ["microsecond", "2000-05-02T12:34:56.123456"],
+ ["nanosecond", "2000-05-02T12:34:56.123456789"],
+ ],
+ "subseconds toString"
+);
+
+test(
+ new Temporal.PlainDateTime(2000, 5, 2, 12, 34),
+ [
+ ["minute", "2000-05-02T12:34"],
+ ["second", "2000-05-02T12:34:00"],
+ ["millisecond", "2000-05-02T12:34:00.000"],
+ ["microsecond", "2000-05-02T12:34:00.000000"],
+ ["nanosecond", "2000-05-02T12:34:00.000000000"],
+ ],
+ "whole minutes toString"
+);
+
+const notValid = [
+ "era",
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+];
+
+notValid.forEach((smallestUnit) => {
+ assert.throws(RangeError, () => datetime.toString({ smallestUnit }),
+ `"${smallestUnit}" is not a valid unit for the smallestUnit option`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..3a6a294126
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-wrong-type.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => datetime.toString({ smallestUnit }),
+ (result, descr) => assert.sameValue(result, "2000-05-02T12:34:56.123987", descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/year-format.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/year-format.js
new file mode 100644
index 0000000000..5815ef6a99
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/year-format.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Verify that the year is appropriately formatted as 4 or 6 digits
+features: [Temporal]
+---*/
+
+let instance = new Temporal.PlainDateTime(-100000, 12, 3, 4, 56, 7, 890);
+assert.sameValue(instance.toString(), "-100000-12-03T04:56:07.89", "large negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDateTime(-10000, 4, 5, 6, 7, 8, 910);
+assert.sameValue(instance.toString(), "-010000-04-05T06:07:08.91", "smallest 5-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDateTime(-9999, 6, 7, 8, 9, 10, 987);
+assert.sameValue(instance.toString(), "-009999-06-07T08:09:10.987", "largest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDateTime(-1000, 8, 9, 10, 9, 8, 765);
+assert.sameValue(instance.toString(), "-001000-08-09T10:09:08.765", "smallest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDateTime(-999, 10, 9, 8, 7, 6, 543);
+assert.sameValue(instance.toString(), "-000999-10-09T08:07:06.543", "largest 3-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDateTime(-1, 8, 7, 6, 54, 32, 100);
+assert.sameValue(instance.toString(), "-000001-08-07T06:54:32.1", "year -1 formatted as 6-digit");
+
+instance = new Temporal.PlainDateTime(0, 6, 5, 4, 32, 10, 123);
+assert.sameValue(instance.toString(), "0000-06-05T04:32:10.123", "year 0 formatted as 4-digit");
+
+instance = new Temporal.PlainDateTime(1, 4, 3, 21, 0, 12, 345);
+assert.sameValue(instance.toString(), "0001-04-03T21:00:12.345", "year 1 formatted as 4-digit");
+
+instance = new Temporal.PlainDateTime(999, 2, 10, 12, 34, 56, 789);
+assert.sameValue(instance.toString(), "0999-02-10T12:34:56.789", "largest 3-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainDateTime(1000, 1, 23, 4, 56, 7, 890);
+assert.sameValue(instance.toString(), "1000-01-23T04:56:07.89", "smallest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainDateTime(9999, 4, 5, 6, 7, 8, 910);
+assert.sameValue(instance.toString(), "9999-04-05T06:07:08.91", "largest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainDateTime(10000, 6, 7, 8, 9, 10, 987);
+assert.sameValue(instance.toString(), "+010000-06-07T08:09:10.987", "smallest 5-digit positive year formatted as 6-digit");
+
+instance = new Temporal.PlainDateTime(100000, 8, 9, 10, 9, 8, 765);
+assert.sameValue(instance.toString(), "+100000-08-09T10:09:08.765", "large positive year formatted as 6-digit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toStringTag/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toStringTag/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toStringTag/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toStringTag/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toStringTag/prop-desc.js
new file mode 100644
index 0000000000..cf2327d830
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toStringTag/prop-desc.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype-@@tostringtag
+description: The @@toStringTag property of Temporal.PlainDateTime
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype, Symbol.toStringTag, {
+ value: "Temporal.PlainDateTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toStringTag/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toStringTag/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toStringTag/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/balance-negative-time-units.js
new file mode 100644
index 0000000000..29ab26b63e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/balance-negative-time-units.js
@@ -0,0 +1,48 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-addtime step 8:
+ 8. Return ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-adddatetime step 1:
+ 1. Let _timeResult_ be ? AddTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
+ sec-temporal-builtintimezonegetinstantfor step 13.a:
+ a. Let _earlier_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], 0, 0, 0, 0, 0, 0, 0, 0, 0, −_nanoseconds_, *"constrain"*).
+ sec-temporal.plaindatetime.prototype.tozoneddatetime step 6:
+ 6. Let _instant_ be BuiltinTimeZoneGetInstantFor(_timeZone_, _dateTime_, _disambiguation_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const shiftInstant = new Temporal.Instant(3661_001_001_001n);
+const tz = TemporalHelpers.oneShiftTimeZone(shiftInstant, 2);
+const datetime = new Temporal.PlainDateTime(1970, 1, 1, 1, 1, 1, 1, 1, 1);
+
+// This code path is encountered if disambiguation is `earlier` and the shift is
+// a spring-forward change
+datetime.toZonedDateTime(tz, { disambiguation: "earlier" });
+
+const expected = [
+ "1970-01-01T01:01:01.001001001",
+ "1970-01-01T01:01:01.001000999",
+];
+assert.compareArray(tz.getPossibleInstantsForCalledWith, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/basic.js
new file mode 100644
index 0000000000..9abaecb1c5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/basic.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: Straightforward case of using UTC
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(2020, 1, 1, 0, 0);
+const zdt = dt.toZonedDateTime("UTC");
+
+assert.sameValue(zdt.epochNanoseconds, 1577836800000000000n, "nanoseconds");
+assert.sameValue(zdt.calendarId, "iso8601", "calendar");
+assert.sameValue(zdt.timeZoneId, "UTC", "timezone");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/branding.js
new file mode 100644
index 0000000000..19ebf25c4a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toZonedDateTime = Temporal.PlainDateTime.prototype.toZonedDateTime;
+
+assert.sameValue(typeof toZonedDateTime, "function");
+
+const args = [new Temporal.TimeZone("UTC")];
+
+assert.throws(TypeError, () => toZonedDateTime.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => toZonedDateTime.apply(null, args), "null");
+assert.throws(TypeError, () => toZonedDateTime.apply(true, args), "true");
+assert.throws(TypeError, () => toZonedDateTime.apply("", args), "empty string");
+assert.throws(TypeError, () => toZonedDateTime.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => toZonedDateTime.apply(1, args), "1");
+assert.throws(TypeError, () => toZonedDateTime.apply({}, args), "plain object");
+assert.throws(TypeError, () => toZonedDateTime.apply(Temporal.PlainDateTime, args), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => toZonedDateTime.apply(Temporal.PlainDateTime.prototype, args), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/builtin.js
new file mode 100644
index 0000000000..0480fffbb9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: >
+ Tests that Temporal.PlainDateTime.prototype.toZonedDateTime
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.toZonedDateTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.toZonedDateTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.toZonedDateTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.toZonedDateTime.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguate-empty-possible-instants-with-datetime-near-limits.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguate-empty-possible-instants-with-datetime-near-limits.js
new file mode 100644
index 0000000000..a164297267
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguate-empty-possible-instants-with-datetime-near-limits.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: >
+ Throws when at minimum resp. maximum value and possible instants is an empty List.
+info: |
+ DisambiguatePossibleInstants ( possibleInstants, timeZone, dateTime, disambiguation )
+
+ ...
+ 9. If ! IsValidEpochNanoseconds(dayBeforeNs) is false, throw a RangeError exception.
+ ...
+ 12. If ! IsValidEpochNanoseconds(dayAfterNs) is false, throw a RangeError exception.
+ ...
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ getPossibleInstantsFor() {
+ return [];
+ }
+}
+
+var tz = new TZ("UTC");
+var min = new Temporal.PlainDateTime(-271821, 4, 20);
+var max = new Temporal.PlainDateTime(275760, 9, 13);
+
+assert.throws(RangeError, () => min.toZonedDateTime(tz), "minimum date-time");
+assert.throws(RangeError, () => max.toZonedDateTime(tz), "maximum date-time");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-invalid-string.js
new file mode 100644
index 0000000000..45d0f0f4c6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-invalid-string.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: RangeError thrown when disambiguation option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaldisambiguation step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*).
+ sec-temporal.plaindatetime.prototype.tozoneddatetime step 5:
+ 5. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2001, 9, 9, 1, 46, 40, 987, 654, 321);
+const timeZone = new Temporal.TimeZone("UTC");
+const invalidStrings = ["obviously bad", "", "EARLIER", "earlıer", "late\u0131r", "reject\0"];
+invalidStrings.forEach((s) => {
+ assert.throws(
+ RangeError,
+ () => datetime.toZonedDateTime(timeZone, { disambiguation: s }),
+ `invalid disambiguation string (${s})`);
+});
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-undefined.js
new file mode 100644
index 0000000000..81ae9ac9f9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-undefined.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: Fallback value for disambiguation option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaldisambiguation step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*).
+ sec-temporal.plaindatetime.prototype.tozoneddatetime step 5:
+ 5. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const timeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const springForwardDatetime = new Temporal.PlainDateTime(2000, 4, 2, 2, 30);
+const fallBackDatetime = new Temporal.PlainDateTime(2000, 10, 29, 1, 30);
+
+[
+ [springForwardDatetime, 954671400_000_000_000n],
+ [fallBackDatetime, 972808200_000_000_000n],
+].forEach(([datetime, expected]) => {
+ const explicit = datetime.toZonedDateTime(timeZone, { disambiguation: undefined });
+ assert.sameValue(explicit.epochNanoseconds, expected, "default disambiguation is compatible");
+ const implicit = datetime.toZonedDateTime(timeZone, {});
+ assert.sameValue(implicit.epochNanoseconds, expected, "default disambiguation is compatible");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-wrong-type.js
new file mode 100644
index 0000000000..8f60ebf750
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-wrong-type.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: Type conversions for disambiguation option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaldisambiguation step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*).
+ sec-temporal.plaindatetime.prototype.tozoneddatetime step 5:
+ 5. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2001, 9, 9, 1, 46, 40, 987, 654, 321);
+const timeZone = new Temporal.TimeZone("UTC");
+TemporalHelpers.checkStringOptionWrongType("disambiguation", "compatible",
+ (disambiguation) => datetime.toZonedDateTime(timeZone, { disambiguation }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_987_654_321n, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/getpossibleinstantsfor-called-with-iso8601-calendar.js
new file mode 100644
index 0000000000..c0fa3c570d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/getpossibleinstantsfor-called-with-iso8601-calendar.js
@@ -0,0 +1,58 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: >
+ Time zone's getPossibleInstantsFor is called with a PlainDateTime with the
+ built-in ISO 8601 calendar
+features: [Temporal]
+info: |
+ DisambiguatePossibleInstants:
+ 2. Let _n_ be _possibleInstants_'s length.
+ ...
+ 5. Assert: _n_ = 0.
+ ...
+ 19. If _disambiguation_ is *"earlier"*, then
+ ...
+ c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_).
+ ...
+ 20. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ ...
+ 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_).
+---*/
+
+class SkippedDateTime extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ this.calls = 0;
+ }
+
+ getPossibleInstantsFor(dateTime) {
+ // Calls occur in pairs. For the first one return no possible instants so
+ // that DisambiguatePossibleInstants will call it again
+ if (this.calls++ % 2 == 0) {
+ return [];
+ }
+
+ assert.sameValue(
+ dateTime.getISOFields().calendar,
+ "iso8601",
+ "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar"
+ );
+ return super.getPossibleInstantsFor(dateTime);
+ }
+}
+
+const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601");
+const timeZone = new SkippedDateTime();
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0, nonBuiltinISOCalendar);
+instance.toZonedDateTime(timeZone);
+
+assert.sameValue(timeZone.calls, 2, "getPossibleInstantsFor should have been called 2 times");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/invalid-instant.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/invalid-instant.js
new file mode 100644
index 0000000000..db7708bac3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/invalid-instant.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: Convert to zoned datetime outside valid range
+features: [Temporal]
+---*/
+
+const max = new Temporal.PlainDateTime(275760, 9, 13, 23, 59, 59, 999, 999, 999);
+const min = new Temporal.PlainDateTime(-271821, 4, 19, 0, 0, 0, 0, 0, 1);
+
+assert.throws(
+ RangeError,
+ () => max.toZonedDateTime("UTC"),
+ "outside of Instant range (too big)"
+);
+
+assert.throws(
+ RangeError,
+ () => min.toZonedDateTime("UTC"),
+ "outside of Instant range (too small)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/length.js
new file mode 100644
index 0000000000..e568058c93
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: Temporal.PlainDateTime.prototype.toZonedDateTime.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toZonedDateTime, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/multiple-instants.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/multiple-instants.js
new file mode 100644
index 0000000000..c8ddda8755
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/multiple-instants.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: Checking disambiguation options for daylight savings time changes
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tz = TemporalHelpers.springForwardFallBackTimeZone();
+
+const dt1 = new Temporal.PlainDateTime(2000, 4, 2, 2);
+
+const zdt1 = dt1.toZonedDateTime(tz);
+const zdt1_compatible = dt1.toZonedDateTime(tz, { disambiguation: "compatible" });
+const zdt1_earlier = dt1.toZonedDateTime(tz, { disambiguation: "earlier" });
+const zdt1_later = dt1.toZonedDateTime(tz, { disambiguation: "later" });
+
+assert.sameValue(zdt1.epochNanoseconds, 954669600000000000n, "Fall DST (no disambiguation)");
+assert.sameValue(zdt1_compatible.epochNanoseconds, 954669600000000000n, "Fall DST (disambiguation = compatible)");
+assert.sameValue(zdt1_earlier.epochNanoseconds, 954666000000000000n, "Fall DST (disambiguation = earlier)");
+assert.sameValue(zdt1_later.epochNanoseconds, 954669600000000000n, "Fall DST (disambiguation = later)");
+
+assert.throws(
+ RangeError,
+ () => dt1.toZonedDateTime(tz, { disambiguation: "reject" }),
+ "Fall DST (disambiguation = reject)"
+);
+
+const dt2 = new Temporal.PlainDateTime(2000, 10, 29, 1);
+
+const zdt2 = dt2.toZonedDateTime(tz);
+const zdt2_compatible = dt2.toZonedDateTime(tz, { disambiguation: "compatible" });
+const zdt2_earlier = dt2.toZonedDateTime(tz, { disambiguation: "earlier" });
+const zdt2_later = dt2.toZonedDateTime(tz, { disambiguation: "later" });
+
+assert.sameValue(zdt2.epochNanoseconds, 972806400000000000n, "Spring DST (no disambiguation)");
+assert.sameValue(zdt2_compatible.epochNanoseconds, 972806400000000000n, "Spring DST (disambiguation = compatible)");
+assert.sameValue(zdt2_earlier.epochNanoseconds, 972806400000000000n, "Spring DST (disambiguation = earlier)");
+assert.sameValue(zdt2_later.epochNanoseconds, 972810000000000000n, "Spring DST (disambiguation = later)");
+
+assert.throws(
+ RangeError,
+ () => dt2.toZonedDateTime(tz, { disambiguation: "reject" }),
+ "Spring DST (disambiguation = reject)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/name.js
new file mode 100644
index 0000000000..20ff417853
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: Temporal.PlainDateTime.prototype.toZonedDateTime.name is "toZonedDateTime".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toZonedDateTime, "name", {
+ value: "toZonedDateTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/not-a-constructor.js
new file mode 100644
index 0000000000..2aaece804b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: >
+ Temporal.PlainDateTime.prototype.toZonedDateTime does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.toZonedDateTime();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.toZonedDateTime), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.toZonedDateTime)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/options-object.js
new file mode 100644
index 0000000000..d6d317e545
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/options-object.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: Empty or a function object may be used as options
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+
+const result1 = instance.toZonedDateTime("UTC", {});
+assert.sameValue(
+ result1.epochNanoseconds, 957225600000000000n,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.toZonedDateTime("UTC", () => {});
+assert.sameValue(
+ result2.epochNanoseconds, 957225600000000000n,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/options-undefined.js
new file mode 100644
index 0000000000..567cd149f4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/options-undefined.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+includes: [temporalHelpers.js]
+description: Verify that undefined options are handled correctly.
+features: [BigInt, Temporal]
+---*/
+
+const datetimeEarlier = new Temporal.PlainDateTime(2000, 10, 29, 1, 34, 56, 987, 654, 321);
+const datetimeLater = new Temporal.PlainDateTime(2000, 4, 2, 2, 34, 56, 987, 654, 321);
+const timeZone = TemporalHelpers.springForwardFallBackTimeZone();
+
+[
+ [datetimeEarlier, 972808496987654321n],
+ [datetimeLater, 954671696987654321n],
+].forEach(([datetime, expected]) => {
+ const explicit = datetime.toZonedDateTime(timeZone, undefined);
+ assert.sameValue(explicit.epochNanoseconds, expected, "default disambiguation is compatible");
+
+ const implicit = datetime.toZonedDateTime(timeZone);
+ assert.sameValue(implicit.epochNanoseconds, expected, "default disambiguation is compatible");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/options-wrong-type.js
new file mode 100644
index 0000000000..255c024a64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/options-wrong-type.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.toZonedDateTime("UTC", value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/order-of-operations.js
new file mode 100644
index 0000000000..8b01064161
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/order-of-operations.js
@@ -0,0 +1,65 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: Properties on an object passed to toZonedDateTime() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalTimeZoneSlotValue
+ "has timeZone.getOffsetNanosecondsFor",
+ "has timeZone.getPossibleInstantsFor",
+ "has timeZone.id",
+ // ToTemporalDisambiguation
+ "get options.disambiguation",
+ "get options.disambiguation.toString",
+ "call options.disambiguation.toString",
+ // lookup in PlainDateTime.p.toZonedDateTime
+ "get timeZone.getOffsetNanosecondsFor",
+ "get timeZone.getPossibleInstantsFor",
+ // GetInstantFor
+ "call timeZone.getPossibleInstantsFor",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+const fallBackInstance = new Temporal.PlainDateTime(2000, 10, 29, 1, 30, 0, 0, 0, 0, calendar);
+const springForwardInstance = new Temporal.PlainDateTime(2000, 4, 2, 2, 30, 0, 0, 0, 0, calendar);
+// clear observable operations that occurred during the constructor calls
+actual.splice(0);
+
+const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "timeZone", {
+ getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor,
+ getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor,
+});
+
+const options = TemporalHelpers.propertyBagObserver(actual, { disambiguation: "compatible" }, "options");
+
+instance.toZonedDateTime(timeZone, options);
+assert.compareArray(actual, expected, "order of operations at normal wall-clock time");
+actual.splice(0); // clear
+
+fallBackInstance.toZonedDateTime(timeZone, options);
+assert.compareArray(actual, expected, "order of operations at repeated wall-clock time");
+actual.splice(0); // clear
+
+springForwardInstance.toZonedDateTime(timeZone, options);
+assert.compareArray(actual, expected.concat([
+ "call timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getPossibleInstantsFor",
+]), "order of operations at skipped wall-clock time");
+actual.splice(0); // clear
+
+const rejectOptions = TemporalHelpers.propertyBagObserver(actual, { disambiguation: "reject" }, "options");
+assert.throws(RangeError, () => springForwardInstance.toZonedDateTime(timeZone, rejectOptions));
+assert.compareArray(actual, expected, "order of operations at skipped wall-clock time with disambiguation: reject");
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/plain-custom-timezone.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/plain-custom-timezone.js
new file mode 100644
index 0000000000..15191200d0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/plain-custom-timezone.js
@@ -0,0 +1,52 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: TimeZone.getPossibleInstantsFor called after processing timeZone and options
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "has timeZone.getOffsetNanosecondsFor",
+ "has timeZone.getPossibleInstantsFor",
+ "has timeZone.id",
+ "get options.disambiguation",
+ "get options.disambiguation.toString",
+ "call options.disambiguation.toString",
+ "get timeZone.getOffsetNanosecondsFor",
+ "get timeZone.getPossibleInstantsFor",
+ "call timeZone.getPossibleInstantsFor",
+];
+
+Object.defineProperty(Temporal.TimeZone, "from", {
+ get() {
+ actual.push("get Temporal.TimeZone.from");
+ return undefined;
+ },
+});
+
+const dateTime = Temporal.PlainDateTime.from("1975-02-02T14:25:36.123456789");
+const instant = Temporal.Instant.fromEpochNanoseconds(-205156799012345679n);
+
+const options = TemporalHelpers.propertyBagObserver(actual, { disambiguation: "reject" }, "options");
+
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "timeZone", {
+ getPossibleInstantsFor(dateTimeArg) {
+ assert.sameValue(dateTimeArg, dateTime);
+ return [instant];
+ },
+});
+
+const result = dateTime.toZonedDateTime(timeZone, options);
+assert.sameValue(result.epochNanoseconds, instant.epochNanoseconds);
+assert.sameValue(result.getTimeZone(), timeZone);
+
+assert.compareArray(actual, expected);
+
+assert.sameValue(result.getISOFields().calendar, dateTime.getISOFields().calendar);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/plain-date-time-near-limits.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/plain-date-time-near-limits.js
new file mode 100644
index 0000000000..75a3510938
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/plain-date-time-near-limits.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: >
+ Throws a RangeError if the date/time value is outside the instant limits
+info: |
+ Temporal.PlainDateTime.prototype.toZonedDateTime ( temporalTimeZoneLike [ , options ] )
+ ...
+ 6. Let instant be ? BuiltinTimeZoneGetInstantFor(timeZone, dateTime, disambiguation).
+ ...
+features: [Temporal]
+---*/
+
+// Try to create from the minimum date-time.
+{
+ let dt = new Temporal.PlainDateTime(-271821, 4, 19, 0, 0, 0, 0, 0, 1);
+ assert.throws(RangeError, () => dt.toZonedDateTime("UTC"));
+}
+{
+ let dt = new Temporal.PlainDateTime(-271821, 4, 19, 1, 0, 0, 0, 0, 0);
+ assert.throws(RangeError, () => dt.toZonedDateTime("UTC"));
+}
+
+// Try to create from the maximum date-time.
+{
+ let dt = new Temporal.PlainDateTime(275760, 9, 13, 0, 0, 0, 0, 0, 1);
+ assert.throws(RangeError, () => dt.toZonedDateTime("UTC"));
+}
+{
+ let dt = new Temporal.PlainDateTime(275760, 9, 13, 1, 0, 0, 0, 0, 0);
+ assert.throws(RangeError, () => dt.toZonedDateTime("UTC"));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/prop-desc.js
new file mode 100644
index 0000000000..f485224197
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: The "toZonedDateTime" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.toZonedDateTime,
+ "function",
+ "`typeof PlainDateTime.prototype.toZonedDateTime` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "toZonedDateTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-case-insensitive.js
new file mode 100644
index 0000000000..06e6ad0128
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-case-insensitive.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: Time zone names are case insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+
+const timeZone = 'uTc';
+const result = instance.toZonedDateTime(timeZone);
+assert.sameValue(result.timeZoneId, 'UTC', `Time zone created from string "${timeZone}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..b57c3cf7f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ assert.throws(RangeError, () => datetime.toZonedDateTime(timeZone));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..047ccfaa4f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.toZonedDateTime(timeZone),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..17f0be600c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ assert.throws(RangeError, () => datetime.toZonedDateTime(timeZone));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..bcba7af74a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ assert.throws(TypeError, () => datetime.toZonedDateTime(timeZone));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 0000000000..a2b6ec45ac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.plaindatetime.prototype.tozoneddatetime step 6:
+ 6. Let _instant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _temporalDateTime_, *"compatible"*).
+ sec-temporal-builtintimezonegetinstantfor step 1:
+ 1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-builtintimezonegetinstantfor step 14:
+ 14. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ sec-temporal-builtintimezonegetinstantfor step 16:
+ 16. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _later_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected1 = [
+ "2000-05-02T12:34:56.987654321",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ datetime.toZonedDateTime(timeZone);
+}, expected1);
+
+// Same, but test the other path where the time doesn't exist and
+// GetPossibleInstantsFor is called again on a later time
+
+const expected2 = [
+ "2030-01-01T00:30:00",
+ "2030-01-01T01:30:00",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const datetime = new Temporal.PlainDateTime(2030, 1, 1, 0, 30);
+ datetime.toZonedDateTime(timeZone);
+}, expected2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-datetime.js
new file mode 100644
index 0000000000..c4e5a05eec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-datetime.js
@@ -0,0 +1,65 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => instance.toZonedDateTime(timeZone), "bare date-time string is not a time zone");
+
+[
+ "2021-08-19T17:30-07:00:01",
+ "2021-08-19T17:30-07:00:00",
+ "2021-08-19T17:30-07:00:00.1",
+ "2021-08-19T17:30-07:00:00.0",
+ "2021-08-19T17:30-07:00:00.01",
+ "2021-08-19T17:30-07:00:00.00",
+ "2021-08-19T17:30-07:00:00.001",
+ "2021-08-19T17:30-07:00:00.000",
+ "2021-08-19T17:30-07:00:00.0001",
+ "2021-08-19T17:30-07:00:00.0000",
+ "2021-08-19T17:30-07:00:00.00001",
+ "2021-08-19T17:30-07:00:00.00000",
+ "2021-08-19T17:30-07:00:00.000001",
+ "2021-08-19T17:30-07:00:00.000000",
+ "2021-08-19T17:30-07:00:00.0000001",
+ "2021-08-19T17:30-07:00:00.0000000",
+ "2021-08-19T17:30-07:00:00.00000001",
+ "2021-08-19T17:30-07:00:00.00000000",
+ "2021-08-19T17:30-07:00:00.000000001",
+ "2021-08-19T17:30-07:00:00.000000000",
+].forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime(timeZone),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone`
+ );
+});
+
+timeZone = "2021-08-19T17:30Z";
+const result1 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result1.timeZoneId, "UTC", "date-time + Z is UTC time zone");
+
+timeZone = "2021-08-19T17:30-07:00";
+const result2 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result2.timeZoneId, "-07:00", "date-time + offset is the offset time zone");
+
+timeZone = "2021-08-19T17:30[UTC]";
+const result3 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result3.timeZoneId, "UTC", "date-time + IANA annotation is the IANA time zone");
+
+timeZone = "2021-08-19T17:30Z[UTC]";
+const result4 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result4.timeZoneId, "UTC", "date-time + Z + IANA annotation is the IANA time zone");
+
+timeZone = "2021-08-19T17:30-07:00[UTC]";
+const result5 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result5.timeZoneId, "UTC", "date-time + offset + IANA annotation is the IANA time zone");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-leap-second.js
new file mode 100644
index 0000000000..82b361d540
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-leap-second.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: Leap second is a valid ISO string for TimeZone
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
+
+const result = instance.toZonedDateTime(timeZone);
+assert.sameValue(result.timeZoneId, "UTC", "leap second is a valid ISO string for TimeZone");
+
+timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
+assert.throws(RangeError, () => instance.toZonedDateTime(timeZone), "leap second in time zone name not valid");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-multiple-offsets.js
new file mode 100644
index 0000000000..a31838d6a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-multiple-offsets.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
+
+const result = instance.toZonedDateTime(timeZone);
+assert.sameValue(result.timeZoneId, "+01:46", "Time zone string determined from bracket name");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-year-zero.js
new file mode 100644
index 0000000000..0861ca32a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-year-zero.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+invalidStrings.forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime(timeZone),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string.js
new file mode 100644
index 0000000000..52ed9f56d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: Time zone IDs are valid input for a time zone
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+
+["UTC", "+01:30"].forEach((timeZone) => {
+ const result = instance.toZonedDateTime(timeZone);
+ assert.sameValue(result.getISOFields().timeZone, timeZone, `time zone slot should store string "${timeZone}"`);
+});
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-wrong-type.js
new file mode 100644
index 0000000000..80f945b8bb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-wrong-type.js
@@ -0,0 +1,42 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [timeZone, description] of primitiveTests) {
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => instance.toZonedDateTime(timeZone),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "object not implementing time zone protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.toZonedDateTime(timeZone), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..3cb35dfea1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const arg = { year: 2000, month: 5, day: 2, hour: 21, minute: 43, second: 5, calendar: "iso8601" };
+instance.until(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-number.js
new file mode 100644
index 0000000000..bed336d5b5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-number.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: A number cannot be used in place of a Temporal.PlainDateTime
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.until(arg),
+ `A number (${arg}) is not a valid ISO string for PlainDateTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-object.js
new file mode 100644
index 0000000000..6f71ba66ad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-object.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Plain objects are accepted as an argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+
+TemporalHelpers.assertDuration(
+ dt.until({ year: 2019, month: 10, day: 29, hour: 10 }),
+ 0, 0, 0, 15684, 18, 36, 29, 876, 543, 211,
+ "casts argument (plain object)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-plaindate.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-plaindate.js
new file mode 100644
index 0000000000..3a9f46bbd6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-plaindate.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Fast path for converting Temporal.PlainDate to Temporal.PlainDateTime by reading internal slots
+info: |
+ sec-temporal.plaindatetime.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.b:
+ b. If _item_ has an [[InitializedTemporalDate]] internal slot, then
+ i. Return ? CreateTemporalDateTime(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], 0, 0, 0, 0, 0, 0, _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalPlainDateTimeFastPath((date, calendar) => {
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 0, 0, 0, 987, 654, 321, calendar);
+ const result = datetime.until(date);
+ assert.sameValue(result.total({ unit: "nanoseconds" }), -987654321, "PlainDate is converted to midnight");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..fd713acf45
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: The calendar name is case-insensitive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18);
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.until(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..7ecf13b3a7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Leap second is a valid ISO string for a calendar in a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18);
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.until(arg);
+TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..cf43c5a168
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-number.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.until(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..a9879a2e99
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-string.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18);
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.until(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..d3d8cb74f4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.until(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.until(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..099de48b51
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-year-zero.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..e65aab926c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-calendar-annotation.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[u-ca=iso8601]", "without time zone"],
+ ["1976-11-18T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["1976-11-18T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["1976-11-18T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["1976-11-18T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..16d8c27c45
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..eb9a8f4dd4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-date-with-utc-offset.js
@@ -0,0 +1,49 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+const validStrings = [
+ "1976-11-18T15:23+00:00",
+ "1976-11-18T15:23+00:00[UTC]",
+ "1976-11-18T15:23+00:00[!UTC]",
+ "1976-11-18T15:23-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.until(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `"${arg}" is a valid UTC offset with time for PlainDateTime`
+ );
+}
+
+const invalidStrings = [
+ "2022-09-15Z",
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDateTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..6c373cfff4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-multiple-calendar.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..3d9b9e28f5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-multiple-time-zone.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[UTC][UTC]",
+ "1970-01-01T00:00[!UTC][UTC]",
+ "1970-01-01T00:00[UTC][!UTC]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00[UTC][foo=bar][UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-time-separators.js
new file mode 100644
index 0000000000..320238e6a2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-time-separators.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23", "uppercase T"],
+ ["1976-11-18t15:23", "lowercase T"],
+ ["1976-11-18 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..f20fd0bdcd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-time-zone-annotation.js
@@ -0,0 +1,35 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[Asia/Kolkata]", "named, with no offset"],
+ ["1976-11-18T15:23[!Europe/Vienna]", "named, with ! and no offset"],
+ ["1976-11-18T15:23[+00:00]", "numeric, with no offset"],
+ ["1976-11-18T15:23[!-02:30]", "numeric, with ! and no offset"],
+ ["1976-11-18T15:23+00:00[UTC]", "named, with offset"],
+ ["1976-11-18T15:23+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["1976-11-18T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["1976-11-18T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..a438aec643
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-unknown-annotation.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[foo=bar]", "alone"],
+ ["1976-11-18T15:23[UTC][foo=bar]", "with time zone"],
+ ["1976-11-18T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["1976-11-18T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["1976-11-18T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..97129a5ba3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-with-utc-designator.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: RangeError thrown if a string with UTC designator is used as a PlainDateTime
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ "String with UTC designator should not be valid as a PlainDateTime"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string.js
new file mode 100644
index 0000000000..714f69d177
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Date-like strings are accepted
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+
+TemporalHelpers.assertDuration(
+ dt.until("2019-10-29T10:46:38.271986102"),
+ 0, 0, 0, 15684, 19, 23, 8, 148, 529, 313,
+ "casts argument (string)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-wrong-type.js
new file mode 100644
index 0000000000..58247233eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-wrong-type.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDateTime
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => instance.until(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDateTime, "Temporal.PlainDateTime, object"],
+ [Temporal.PlainDateTime.prototype, "Temporal.PlainDateTime.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.until(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 0000000000..29b515f78c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,46 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Negative time fields are balanced upwards if the argument is given as ZonedDateTime
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-temporal-totemporaldatetime step 3.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ ii. 1. Return ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
+ sec-temporal.plaindatetime.prototype.until step 3:
+ 3. Set _other_ ? ToTemporalDateTime(_other_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// This code path is encountered if the time zone offset is negative and its
+// absolute value in nanoseconds is greater than the nanosecond field of the
+// exact time's epoch parts
+const tz = TemporalHelpers.specificOffsetTimeZone(-2);
+const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
+
+const diff = new Temporal.PlainDateTime(1970, 1, 1).until(datetime);
+
+TemporalHelpers.assertDuration(diff, 0, 0, 0, 0, 1, 1, 1, 1, 0, 999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..9f2b2bcea0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+const result = instance.until(datetime);
+TemporalHelpers.assertDuration(result, 0, 0, 0, -11239, -22, -40, -10, -987, -654, -320);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..99535f976c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.until(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..0455bd5317
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => plain.until(zoned),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..459ee0a25d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.until(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..5d5a71b0da
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => plain.until(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/balance-negative-duration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/balance-negative-duration.js
new file mode 100644
index 0000000000..9b3abb95dc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/balance-negative-duration.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Negative durations are balanced correctly by the modulo operation in NanosecondsToDays
+info: |
+ sec-temporal-nanosecondstodays step 6:
+ 6. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Return the new Record { ..., [[Nanoseconds]]: abs(_nanoseconds_) modulo _dayLengthNs_ × _sign_, ... }.
+ sec-temporal-balanceduration step 4:
+ 4. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_).
+ sec-temporal-differenceisodatetime steps 7 and 13:
+ 7. If _timeSign_ is -_dateSign_, then
+ ...
+ b. Set _timeDifference_ to ? BalanceDuration(-_timeSign_, _timeDifference_.[[Hours]], _timeDifference_.[[Minutes]], _timeDifference_.[[Seconds]], _timeDifference_.[[Milliseconds]], _timeDifference_.[[Microseconds]], _timeDifference_.[[Nanoseconds]], _largestUnit_).
+ ...
+ 13. Return ? BalanceDuration(_dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], _dateDifference_.[[Days]], _timeDifference_.[[Hours]], _timeDifference_.[[Minutes]], _timeDifference_.[[Seconds]], _timeDifference_.[[Milliseconds]], _timeDifference_.[[Microseconds]], _timeDifference_.[[Nanoseconds]], _largestUnit_).
+ sec-temporal.plaindatetime.prototype.until step 13:
+ 13. Let _diff_ be ? DifferenceISODateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _other_.[[ISOYear]], _other_.[[ISOMonth]], _other_.[[ISODay]], _other_.[[ISOHour]], _other_.[[ISOMinute]], _other_.[[ISOSecond]], _other_.[[ISOMillisecond]], _other_.[[ISOMicrosecond]], _other_.[[ISONanosecond]], _dateTime_.[[Calendar]], _largestUnit_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier1 = new Temporal.PlainDateTime(2000, 5, 2, 9);
+const later1 = new Temporal.PlainDateTime(2000, 5, 5, 10);
+const result1 = later1.until(earlier1, { largestUnit: "day" });
+TemporalHelpers.assertDuration(result1, 0, 0, 0, -3, -1, 0, 0, 0, 0, 0, "date sign == time sign");
+
+const earlier2 = new Temporal.PlainDateTime(2000, 5, 2, 10);
+const later2 = new Temporal.PlainDateTime(2000, 5, 5, 9);
+const result2 = later2.until(earlier2, { largestUnit: "day" });
+TemporalHelpers.assertDuration(result2, 0, 0, 0, -2, -23, 0, 0, 0, 0, 0, "date sign != time sign");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/balance-negative-time-units.js
new file mode 100644
index 0000000000..82cd4276bf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/balance-negative-time-units.js
@@ -0,0 +1,53 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-differencetime step 8:
+ 8. Let _bt_ be ? BalanceTime(_hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
+ sec-temporal-differenceisodatetime step 2:
+ 2. Let _timeDifference_ be ? DifferenceTime(_h1_, _min1_, _s1_, _ms1_, _mus1_, _ns1_, _h2_, _min2_, _s2_, _ms2_, _mus2_, _ns2_).
+ sec-temporal.plaindatetime.prototype.until step 13:
+ 13. Let _diff_ be ? DifferenceISODateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _other_.[[ISOYear]], _other_.[[ISOMonth]], _other_.[[ISODay]], _other_.[[ISOHour]], _other_.[[ISOMinute]], _other_.[[ISOSecond]], _other_.[[ISOMillisecond]], _other_.[[ISOMicrosecond]], _other_.[[ISONanosecond]], _dateTime_.[[Calendar]], _largestUnit_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1996, 5, 2, 1, 1, 1, 1, 1, 1);
+
+const result1 = new Temporal.PlainDateTime(1996, 5, 2, 0, 0, 0, 0, 0, 2).until(datetime);
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
+
+const result2 = new Temporal.PlainDateTime(1996, 5, 2, 0, 0, 0, 0, 2).until(datetime);
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 1, 1, 1, 0, 999, 1, "microseconds balance");
+
+const result3 = new Temporal.PlainDateTime(1996, 5, 2, 0, 0, 0, 2).until(datetime);
+TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 1, 1, 0, 999, 1, 1, "milliseconds balance");
+
+const result4 = new Temporal.PlainDateTime(1996, 5, 2, 0, 0, 2).until(datetime);
+TemporalHelpers.assertDuration(result4, 0, 0, 0, 0, 1, 0, 59, 1, 1, 1, "seconds balance");
+
+const result5 = new Temporal.PlainDateTime(1996, 5, 2, 0, 2).until(datetime);
+TemporalHelpers.assertDuration(result5, 0, 0, 0, 0, 0, 59, 1, 1, 1, 1, "minutes balance");
+
+// This one is different because hours are later balanced again in BalanceDuration
+const result6 = new Temporal.PlainDateTime(1996, 5, 2, 2).until(datetime);
+TemporalHelpers.assertDuration(result6, 0, 0, 0, 0, 0, -58, -58, -998, -998, -999, "hours balance");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/balance.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/balance.js
new file mode 100644
index 0000000000..1950557975
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/balance.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Results with opposite-sign components (e.g. months=1, hours=-1) are balanced correctly
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const a = Temporal.PlainDateTime.from("2017-10-05T08:07:14+00:00[UTC]");
+const b = Temporal.PlainDateTime.from("2021-03-05T03:32:45+00:00[UTC]");
+const c = Temporal.PlainDateTime.from("2021-03-05T09:32:45+00:00[UTC]");
+
+const r1 = a.until(b, { largestUnit: "months" });
+TemporalHelpers.assertDuration(r1, 0, 40, 0, 27, 19, 25, 31, 0, 0, 0, "r1");
+assert.sameValue(a.add(r1).toString(), b.toString(), "a.add(r1)");
+
+const r2 = b.until(a, { largestUnit: "months" });
+TemporalHelpers.assertDuration(r2, 0, -40, 0, -30, -19, -25, -31, 0, 0, 0, "r2");
+assert.sameValue(b.add(r2).toString(), a.toString(), "b.add(r2)");
+
+const r3 = c.until(a, { largestUnit: "months" });
+TemporalHelpers.assertDuration(r3, 0, -41, 0, 0, -1, -25, -31, 0, 0, 0, "r3");
+assert.sameValue(c.add(r3).toString(), a.toString(), "c.add(r3)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/branding.js
new file mode 100644
index 0000000000..72c29bc1d7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const until = Temporal.PlainDateTime.prototype.until;
+
+assert.sameValue(typeof until, "function");
+
+const args = [new Temporal.PlainDateTime(2022, 6, 22)];
+
+assert.throws(TypeError, () => until.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => until.apply(null, args), "null");
+assert.throws(TypeError, () => until.apply(true, args), "true");
+assert.throws(TypeError, () => until.apply("", args), "empty string");
+assert.throws(TypeError, () => until.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => until.apply(1, args), "1");
+assert.throws(TypeError, () => until.apply({}, args), "plain object");
+assert.throws(TypeError, () => until.apply(Temporal.PlainDateTime, args), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => until.apply(Temporal.PlainDateTime.prototype, args), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..d36ba8a1cd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateUntilOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateUntil");
+Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateUntil should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.until(new Temporal.PlainDateTime(2001, 6, 13));
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", dateUntilOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/builtin.js
new file mode 100644
index 0000000000..8db60e1aab
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: >
+ Tests that Temporal.PlainDateTime.prototype.until
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.until),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.until),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.until),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.until.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateadd-called-with-plaindate-instance.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateadd-called-with-plaindate-instance.js
new file mode 100644
index 0000000000..11882b5dca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateadd-called-with-plaindate-instance.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: >
+ relativeTo parameters that are not ZonedDateTime or undefined, are always
+ converted to PlainDate for observable calendar calls
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddPlainDateInstance();
+const instance = new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, 0, 0, calendar);
+instance.until(new Temporal.PlainDateTime(2000, 5, 2, 0, 0, 0, 0, 0, 0, calendar), { smallestUnit: "month" });
+assert(calendar.dateAddCallCount > 0, "assertions in calendar.dateAdd() should have been tested");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..0614563612
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-datefromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.until(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-copy-of-options.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-copy-of-options.js
new file mode 100644
index 0000000000..2bf2e54661
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-copy-of-options.js
@@ -0,0 +1,35 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: The dateUntil() method on the calendar is called with a copy of the options bag
+features: [Temporal]
+---*/
+
+const originalOptions = {
+ largestUnit: "year",
+ shouldBeCopied: {},
+};
+let called = false;
+
+class Calendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateUntil(d1, d2, options) {
+ called = true;
+ assert.notSameValue(options, originalOptions, "options bag should be a copy");
+ assert.sameValue(options.shouldBeCopied, originalOptions.shouldBeCopied, "options bag should be a shallow copy");
+ return new Temporal.Duration();
+ }
+}
+const calendar = new Calendar();
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322, calendar);
+earlier.until(later, originalOptions);
+assert(called, "calendar.dateUntil must be called");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js
new file mode 100644
index 0000000000..c57b59dcdb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: >
+ Calendar.dateUntil method is called with a null-prototype object as the
+ options value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckOptionsPrototypePollution();
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+const argument = new Temporal.PlainDateTime(2022, 6, 14, 18, 21, 36, 660, 690, 387, calendar);
+instance.until(argument, { largestUnit: "months" });
+assert.sameValue(calendar.dateUntilCallCount, 1, "dateUntil should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-plaindate-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-plaindate-calendar.js
new file mode 100644
index 0000000000..cba7264915
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-plaindate-calendar.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: calendar.dateUntil() is passed PlainDate objects with the receiver's calendar
+info: |
+ DifferenceISODateTime ( y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1, y2, mon2, d2, h2, min2, s2, ms2, mus2, ns2, calendar, largestUnit [ , options ] )
+
+ 8. Let _date1_ be ? CreateTemporalDate(_balanceResult_.[[Year]], _balanceResult_.[[Month]], _balanceResult_.[[Day]], _calendar_).
+ 9. Let _date2_ be ? CreateTemporalDate(_y2_, _mon2_, _d2_, _calendar_).
+ 12. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
+features: [Temporal]
+---*/
+
+class Calendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateUntil(d1, d2) {
+ assert.sameValue(d1.getCalendar(), this, "d1.calendar");
+ assert.sameValue(d2.getCalendar(), this, "d2.calendar");
+ return new Temporal.Duration();
+ }
+}
+const calendar = new Calendar();
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322, calendar);
+const result = earlier.until(later);
+assert(result instanceof Temporal.Duration, "result");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 0000000000..6659a56d21
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.plaindatetime.prototype.until step 13:
+ 13. Let _diff_ be ? DifferenceISODateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _other_.[[ISOYear]], _other_.[[ISOMonth]], _other_.[[ISODay]], _other_.[[ISOHour]], _other_.[[ISOMinute]], _other_.[[ISOSecond]], _other_.[[ISOMillisecond]], _other_.[[ISOMicrosecond]], _other_.[[ISONanosecond]], _dateTime_.[[Calendar]], _largestUnit_, _options_).
+ sec-temporal-differenceisodatetime steps 9–11:
+ 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
+ 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_).
+ 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+ const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322, calendar);
+ earlier.until(later, { largestUnit });
+ },
+ {
+ years: ["year"],
+ months: ["month"],
+ weeks: ["week"],
+ days: [],
+ hours: [],
+ minutes: [],
+ seconds: [],
+ milliseconds: [],
+ microseconds: [],
+ nanoseconds: []
+ }
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-fields-iterable.js
new file mode 100644
index 0000000000..1b41916350
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-fields-iterable.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindatetime.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+datetime.until({ year: 2005, month: 6, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-temporal-object.js
new file mode 100644
index 0000000000..f44a965dda
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindatetime.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const date = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, temporalObject);
+ date.until({ year: 2005, month: 6, day: 2, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/casts-argument.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/casts-argument.js
new file mode 100644
index 0000000000..855e0ad714
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/casts-argument.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: String and object arguments get cast
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+
+TemporalHelpers.assertDuration(
+ datetime.until({ year: 2019, month: 10, day: 29, hour: 10 }),
+ 0, 0, 0, 15684, 18, 36, 29, 876, 543, 211,
+ "plain object argument"
+);
+
+TemporalHelpers.assertDuration(
+ datetime.until("2019-10-29T10:46:38.271986102"),
+ 0, 0, 0, 15684, 19, 23, 8, 148, 529, 313,
+ "string argument gets cast"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..648b1afd9b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/constructor-in-calendar-fields.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+assert.throws(RangeError, () => instance.until(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/different-calendars-throws.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/different-calendars-throws.js
new file mode 100644
index 0000000000..24c5e2e39b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/different-calendars-throws.js
@@ -0,0 +1,42 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Using different calendars is not acceptable
+features: [Temporal]
+---*/
+
+const dt1 = new Temporal.PlainDateTime(2000, 1, 1, 0, 0, 0, 0, 0, 0);
+const dt2 = new Temporal.PlainDateTime(2000, 1, 1, 0, 0, 0, 0, 0, 0, {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "custom",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+
+assert.throws(
+ RangeError,
+ () => dt1.until(dt2),
+ "cannot use until with PDTs having different calendars"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..3beced06e9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['hour'], ['microsecond'], ['millisecond'], ['minute'], ['month'], ['monthCode'], ['nanosecond'], ['second'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+ assert.throws(RangeError, () => instance.until(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..70e5f82ce0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/infinity-throws-rangeerror.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindatetime.prototype.until
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15);
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.until({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.until({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/inverse.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/inverse.js
new file mode 100644
index 0000000000..8c77539901
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/inverse.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: The since and until operations act as inverses
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2016, 3, 3, 18);
+
+TemporalHelpers.assertDurationsEqual(dt.until(later), later.since(dt), "until and since act as inverses");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-invalid-string.js
new file mode 100644
index 0000000000..68f7aadcdc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-invalid-string.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 987, 654, 321);
+const badValues = [
+ "era",
+ "eraYear",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string"
+];
+for (const largestUnit of badValues) {
+ assert.throws(RangeError, () => earlier.until(later, { largestUnit }),
+ `"${largestUnit}" is not a valid value for largestUnit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-plurals-accepted.js
new file mode 100644
index 0000000000..744be9778a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-plurals-accepted.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 12, 13, 35, 57, 988, 655, 322);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => earlier.until(later, { largestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-smallestunit-mismatch.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-smallestunit-mismatch.js
new file mode 100644
index 0000000000..a281439e64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-smallestunit-mismatch.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: RangeError thrown when smallestUnit is larger than largestUnit
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 987, 654, 321);
+const units = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+for (let largestIdx = 1; largestIdx < units.length; largestIdx++) {
+ for (let smallestIdx = 0; smallestIdx < largestIdx; smallestIdx++) {
+ const largestUnit = units[largestIdx];
+ const smallestUnit = units[smallestIdx];
+ assert.throws(RangeError, () => earlier.until(later, { largestUnit, smallestUnit }));
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-undefined.js
new file mode 100644
index 0000000000..5d5f650430
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-undefined.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Fallback value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 987, 654, 321);
+
+const explicit = earlier.until(later, { largestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 397, 1, 1, 1, 987, 654, 321, "default largestUnit is day");
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 397, 1, 1, 1, 987, 654, 321, "default largestUnit is day");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-wrong-type.js
new file mode 100644
index 0000000000..a9bdaf9695
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-wrong-type.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 987, 654, 321);
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "year",
+ (largestUnit) => earlier.until(later, { largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 1, 0, 1, 1, 1, 1, 987, 654, 321, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/leap-second.js
new file mode 100644
index 0000000000..7d319f4a4b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/leap-second.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Leap second is a valid ISO string for PlainDateTime
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2016, 12, 31, 23, 59, 59);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.until(arg);
+TemporalHelpers.assertDuration(
+ result1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for PlainDateTime"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.until(arg);
+TemporalHelpers.assertDuration(
+ result2,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "second: 60 is ignored in property bag for PlainDateTime"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/length.js
new file mode 100644
index 0000000000..ae05bf5d55
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Temporal.PlainDateTime.prototype.until.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.until, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/name.js
new file mode 100644
index 0000000000..c07e663445
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Temporal.PlainDateTime.prototype.until.name is "until".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.until, "name", {
+ value: "until",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/no-unnecessary-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/no-unnecessary-units.js
new file mode 100644
index 0000000000..7e311f8dca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/no-unnecessary-units.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Do not return Durations with unnecessary units
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const feb29 = new Temporal.PlainDateTime(2020, 2, 29, 0, 0);
+const feb28 = new Temporal.PlainDateTime(2021, 2, 28, 0, 0);
+
+TemporalHelpers.assertDuration(
+ feb29.until(feb28, { largestUnit: "months" }),
+ 0, 12, 0, 0, 0, 0, 0, 0, 0, 0,
+ "does not include higher units than necessary (largest unit = months)"
+);
+
+TemporalHelpers.assertDuration(
+ feb29.until(feb28, { largestUnit: "years" }),
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "does not include higher units than necessary (largest unit = years)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/not-a-constructor.js
new file mode 100644
index 0000000000..9d88cc5ceb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: >
+ Temporal.PlainDateTime.prototype.until does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.until();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.until), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.until)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-empty.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-empty.js
new file mode 100644
index 0000000000..05233e55f6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-empty.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Empty options are valid
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const feb20 = new Temporal.PlainDateTime(2020, 2, 1, 0, 0);
+const feb21 = new Temporal.PlainDateTime(2021, 2, 1, 0, 0);
+
+TemporalHelpers.assertDuration(feb20.until(feb21, {}),
+ 0, 0, 0, 366, 0, 0, 0, 0, 0, 0,
+ "empty options (plain object) are acceptable");
+
+TemporalHelpers.assertDuration(feb20.until(feb21, () => {}),
+ 0, 0, 0, 366, 0, 0, 0, 0, 0, 0,
+ "empty options (function object) are acceptable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-invalid.js
new file mode 100644
index 0000000000..cecac27183
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-invalid.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: A variety of invalid option arguments
+features: [Temporal, Symbol]
+---*/
+
+const feb20 = new Temporal.PlainDateTime(2020, 2, 1, 0, 0);
+const feb21 = new Temporal.PlainDateTime(2021, 2, 1, 0, 0);
+
+const badOptions = [null, 1, 'obviously invalid', true, Symbol('foo'), 1n];
+badOptions.forEach((bad) => {
+ assert.throws(
+ TypeError,
+ () => feb20.until(feb21, bad),
+ `unacceptable options (${typeof bad})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-undefined.js
new file mode 100644
index 0000000000..df89d800c2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-undefined.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2000, 6, 12, 12, 34, 56, 987, 654, 322);
+
+const explicit = earlier.until(later, undefined);
+assert.sameValue(explicit.years, 0, "default largest unit is days");
+assert.sameValue(explicit.months, 0, "default largest unit is days");
+assert.sameValue(explicit.weeks, 0, "default largest unit is days");
+assert.sameValue(explicit.days, 41, "default largest unit is days");
+assert.sameValue(explicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding");
+
+const implicit = earlier.until(later);
+assert.sameValue(implicit.years, 0, "default largest unit is days");
+assert.sameValue(implicit.months, 0, "default largest unit is days");
+assert.sameValue(implicit.weeks, 0, "default largest unit is days");
+assert.sameValue(implicit.days, 41, "default largest unit is days");
+assert.sameValue(implicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-wrong-type.js
new file mode 100644
index 0000000000..f5098cf0eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-wrong-type.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.until(new Temporal.PlainDateTime(1976, 11, 18), value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/order-of-operations.js
new file mode 100644
index 0000000000..0cc2af79e3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/order-of-operations.js
@@ -0,0 +1,250 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Properties on objects passed to until() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalDateTime
+ "get other.calendar",
+ "has other.calendar.dateAdd",
+ "has other.calendar.dateFromFields",
+ "has other.calendar.dateUntil",
+ "has other.calendar.day",
+ "has other.calendar.dayOfWeek",
+ "has other.calendar.dayOfYear",
+ "has other.calendar.daysInMonth",
+ "has other.calendar.daysInWeek",
+ "has other.calendar.daysInYear",
+ "has other.calendar.fields",
+ "has other.calendar.id",
+ "has other.calendar.inLeapYear",
+ "has other.calendar.mergeFields",
+ "has other.calendar.month",
+ "has other.calendar.monthCode",
+ "has other.calendar.monthDayFromFields",
+ "has other.calendar.monthsInYear",
+ "has other.calendar.weekOfYear",
+ "has other.calendar.year",
+ "has other.calendar.yearMonthFromFields",
+ "has other.calendar.yearOfWeek",
+ "get other.calendar.dateFromFields",
+ "get other.calendar.fields",
+ "call other.calendar.fields",
+ "get other.day",
+ "get other.day.valueOf",
+ "call other.day.valueOf",
+ "get other.hour",
+ "get other.hour.valueOf",
+ "call other.hour.valueOf",
+ "get other.microsecond",
+ "get other.microsecond.valueOf",
+ "call other.microsecond.valueOf",
+ "get other.millisecond",
+ "get other.millisecond.valueOf",
+ "call other.millisecond.valueOf",
+ "get other.minute",
+ "get other.minute.valueOf",
+ "call other.minute.valueOf",
+ "get other.month",
+ "get other.month.valueOf",
+ "call other.month.valueOf",
+ "get other.monthCode",
+ "get other.monthCode.toString",
+ "call other.monthCode.toString",
+ "get other.nanosecond",
+ "get other.nanosecond.valueOf",
+ "call other.nanosecond.valueOf",
+ "get other.second",
+ "get other.second.valueOf",
+ "call other.second.valueOf",
+ "get other.year",
+ "get other.year.valueOf",
+ "call other.year.valueOf",
+ "call other.calendar.dateFromFields",
+ // CalendarEquals
+ "get this.calendar.id",
+ "get other.calendar.id",
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.roundingIncrement",
+ "get options.roundingIncrement",
+ "getOwnPropertyDescriptor options.roundingMode",
+ "get options.roundingMode",
+ "getOwnPropertyDescriptor options.largestUnit",
+ "get options.largestUnit",
+ "getOwnPropertyDescriptor options.smallestUnit",
+ "get options.smallestUnit",
+ "getOwnPropertyDescriptor options.additional",
+ "get options.additional",
+ // GetDifferenceSettings
+ "get options.largestUnit.toString",
+ "call options.largestUnit.toString",
+ "get options.roundingIncrement.valueOf",
+ "call options.roundingIncrement.valueOf",
+ "get options.roundingMode.toString",
+ "call options.roundingMode.toString",
+ "get options.smallestUnit.toString",
+ "call options.smallestUnit.toString",
+];
+const actual = [];
+
+const ownCalendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, ownCalendar);
+
+const otherDateTimePropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 6,
+ monthCode: "M06",
+ day: 2,
+ hour: 1,
+ minute: 46,
+ second: 40,
+ millisecond: 250,
+ microsecond: 500,
+ nanosecond: 750,
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+
+function createOptionsObserver({ smallestUnit = "nanoseconds", largestUnit = "auto", roundingMode = "halfExpand", roundingIncrement = 1 } = {}) {
+ return TemporalHelpers.propertyBagObserver(actual, {
+ // order is significant, due to iterating through properties in order to
+ // copy them to an internal null-prototype object:
+ roundingIncrement,
+ roundingMode,
+ largestUnit,
+ smallestUnit,
+ additional: "property",
+ }, "options");
+}
+
+// clear any observable things that happened while constructing the objects
+actual.splice(0);
+
+// basic order of observable operations with calendar call, without rounding:
+instance.until(otherDateTimePropertyBag, createOptionsObserver({ largestUnit: "years" }));
+assert.compareArray(actual, expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+]), "order of operations");
+actual.splice(0); // clear
+
+// short-circuit for identical objects:
+const identicalPropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 5,
+ monthCode: "M05",
+ day: 2,
+ hour: 12,
+ minute: 34,
+ second: 56,
+ millisecond: 987,
+ microsecond: 654,
+ nanosecond: 321,
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+
+instance.until(identicalPropertyBag, createOptionsObserver());
+assert.compareArray(actual, expected, "order of operations with identical datetimes");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year:
+const expectedOpsForYearRounding = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateAdd", // 12.d
+ "call this.calendar.dateAdd", // 12.f
+ "call this.calendar.dateUntil", // 12.n
+ "call this.calendar.dateAdd", // 12.x MoveRelativeDate
+ // (12.r not called because other units can't add up to >1 year at this point)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 9.c
+ "call this.calendar.dateUntil" // 9.d
+]);
+instance.until(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "years" }));
+assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year and skips a DateUntil call:
+const otherDatePropertyBagSameMonth = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 5,
+ monthCode: "M05",
+ day: 2,
+ hour: 12,
+ minute: 34,
+ second: 56,
+ millisecond: 987,
+ microsecond: 654,
+ nanosecond: 321,
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+const expectedOpsForYearRoundingSameMonth = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateAdd", // 12.d
+ "call this.calendar.dateAdd", // 12.f
+ "call this.calendar.dateAdd", // 12.x MoveRelativeDate
+ // (12.n not called because months and weeks == 0)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 9.c
+ "call this.calendar.dateUntil" // 9.d
+]);
+instance.until(otherDatePropertyBagSameMonth, createOptionsObserver({ smallestUnit: "years" }));
+assert.compareArray(actual, expectedOpsForYearRoundingSameMonth, "order of operations with smallestUnit = years and no excess months/weeks");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest month:
+const expectedOpsForMonthRounding = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateAdd", // 13.c
+ "call this.calendar.dateAdd", // 13.e
+ "call this.calendar.dateUntil", // 13.m
+ "call this.calendar.dateAdd", // 13.w MoveRelativeDate
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 10.d
+ "call this.calendar.dateUntil" // 10.e
+]);
+instance.until(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "months" }));
+assert.compareArray(actual, expectedOpsForMonthRounding, "order of operations with smallestUnit = years");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest week:
+const expectedOpsForWeekRounding = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateUntil", // 14.f
+ "call this.calendar.dateAdd", // 14.p MoveRelativeDate
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 16
+ "call this.calendar.dateUntil" // 17
+]);
+instance.until(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "weeks" }));
+assert.compareArray(actual, expectedOpsForWeekRounding, "order of operations with smallestUnit = weeks");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/prop-desc.js
new file mode 100644
index 0000000000..4b537789d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: The "until" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.until,
+ "function",
+ "`typeof PlainDateTime.prototype.until` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "until", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..dd01f8a68b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/proto-in-calendar-fields.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+assert.throws(RangeError, () => instance.until(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/read-time-fields-before-datefromfields.js
new file mode 100644
index 0000000000..8b50fbddf6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/read-time-fields-before-datefromfields.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.plaindatetime.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.e:
+ e. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const datetime = new Temporal.PlainDateTime(2021, 3, 31, 12, 34, 56, 987, 654, 321);
+const duration = datetime.until({ year: 2021, month: 3, day: 31, calendar });
+
+TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, -12, -34, -56, -987, -654, -321);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/returns-days.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/returns-days.js
new file mode 100644
index 0000000000..f43542929b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/returns-days.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Return days by default
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const feb20 = new Temporal.PlainDateTime(2020, 2, 1, 0, 0);
+const feb21 = new Temporal.PlainDateTime(2021, 2, 1, 0, 0);
+
+TemporalHelpers.assertDuration(
+ feb20.until(feb21, { largestUnit: "auto" }),
+ 0, 0, 0, 366, 0, 0, 0, 0, 0, 0,
+ "defaults to returning days (largest unit = auto)"
+);
+
+TemporalHelpers.assertDuration(
+ feb20.until(feb21, { largestUnit: "days" }),
+ 0, 0, 0, 366, 0, 0, 0, 0, 0, 0,
+ "defaults to returning days (largest unit = days)"
+);
+
+TemporalHelpers.assertDuration(
+ feb20.until(new Temporal.PlainDateTime(2021, 2, 1, 0, 0, 0, 0, 0, 1)),
+ 0, 0, 0, 366, 0, 0, 0, 0, 0, 1,
+ "returns nanoseconds if argument is PDT with non-zero nanoseconds"
+);
+
+const dt = new Temporal.PlainDateTime(2020, 2, 1, 0, 0, 0, 0, 0, 1);
+
+TemporalHelpers.assertDuration(
+ dt.until(feb21),
+ 0, 0, 0, 365, 23, 59, 59, 999, 999, 999,
+ "one nanosecond away from one year away"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/round-cross-unit-boundary.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/round-cross-unit-boundary.js
new file mode 100644
index 0000000000..4a687a2d70
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/round-cross-unit-boundary.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Rounding can cross unit boundaries up to largestUnit
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// Date units
+{
+ const earlier = new Temporal.PlainDateTime(2022, 1, 1);
+ const later = new Temporal.PlainDateTime(2023, 12, 25);
+ const duration = earlier.until(later, { largestUnit: "years", smallestUnit: "months", roundingMode: "expand" });
+ TemporalHelpers.assertDuration(duration, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "1 year 11 months balances to 2 years");
+}
+
+// Time units
+{
+ const earlier = new Temporal.PlainDateTime(2000, 5, 2);
+ const later = new Temporal.PlainDateTime(2000, 5, 2, 1, 59, 59);
+ const duration = earlier.until(later, { largestUnit: "hours", smallestUnit: "minutes", roundingMode: "expand" });
+ TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, "1:59 balances to 2 hours");
+}
+
+// Both
+{
+ const earlier = new Temporal.PlainDateTime(1970, 1, 1);
+ const later = new Temporal.PlainDateTime(1971, 12, 31, 23, 59, 59, 999, 999, 999);
+ const duration = earlier.until(later, { largestUnit: "years", smallestUnit: "microseconds", roundingMode: "expand" });
+ TemporalHelpers.assertDuration(duration, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "rounding up 1 ns balances to 2 years");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/round-negative-duration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/round-negative-duration.js
new file mode 100644
index 0000000000..16bd2b8838
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/round-negative-duration.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Negative durations are rounded correctly by the modulo operation in NanosecondsToDays
+info: |
+ sec-temporal-nanosecondstodays step 6:
+ 6. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Return the new Record { ..., [[Nanoseconds]]: abs(_nanoseconds_) modulo _dayLengthNs_ × _sign_, ... }.
+ sec-temporal-roundduration step 6:
+ 6. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_).
+ sec-temporal.plaindatetime.prototype.until step 14:
+ 14. Let _roundResult_ be ? RoundDuration(_diff_.[[Years]], _diff_.[[Months]], _diff_.[[Weeks]], _diff_.[[Days]], _diff_.[[Hours]], _diff_.[[Minutes]], _diff_.[[Seconds]], _diff_.[[Milliseconds]], _diff_.[[Microseconds]], _diff_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _dateTime_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12);
+const later = new Temporal.PlainDateTime(2000, 5, 5);
+const result = later.until(earlier, { smallestUnit: "day", roundingIncrement: 2 });
+TemporalHelpers.assertDuration(result, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/round-relative-to-receiver.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/round-relative-to-receiver.js
new file mode 100644
index 0000000000..413112eab3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/round-relative-to-receiver.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Rounding happens relative to receiver
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dt1 = new Temporal.PlainDateTime(2019, 1, 1);
+const dt2 = new Temporal.PlainDateTime(2020, 7, 2);
+const options = { smallestUnit: "years", roundingMode: "halfExpand" };
+
+TemporalHelpers.assertDuration(
+ dt1.until(dt2, options),
+ 2, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "rounds relative to the receiver (positive case)"
+);
+
+TemporalHelpers.assertDuration(
+ dt2.until(dt1, options),
+ -1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "rounds relative to the receiver (negative case)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/rounding-zero-year-month-week-length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/rounding-zero-year-month-week-length.js
new file mode 100644
index 0000000000..893e50889c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/rounding-zero-year-month-week-length.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: >
+ A malicious calendar resulting in a year, month, or week length of zero is
+ handled correctly
+info: |
+ RoundDuration
+ 10.z. If _oneYearDays_ = 0, throw a *RangeError* exception.
+ ...
+ 11.z. If _oneMonthDays_ = 0, throw a *RangeError* exception.
+ ...
+ 12.s. If _oneWeekDays_ = 0, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const cal = new class extends Temporal.Calendar {
+ dateAdd(date, duration, options) {
+ // Called several times, last call sets oneYear/Month/WeekDays to 0
+ return new Temporal.PlainDate(1970, 1, 1);
+ }
+}("iso8601");
+
+const dt1 = new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, 0, 0, cal);
+const dt2 = new Temporal.PlainDateTime(1971, 1, 1, 0, 0, 0, 0, 0, 1, cal);
+
+assert.throws(RangeError, () => dt1.until(dt2, { smallestUnit: "years" }), "zero year length handled correctly");
+assert.throws(RangeError, () => dt1.until(dt2, { smallestUnit: "months" }), "zero month length handled correctly");
+assert.throws(RangeError, () => dt1.until(dt2, { smallestUnit: "weeks" }), "zero week length handled correctly");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-basic.js
new file mode 100644
index 0000000000..3196277706
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-basic.js
@@ -0,0 +1,51 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: A variety of rounding increments
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 321);
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, {smallestUnit: "hours", roundingIncrement: 3, roundingMode: "halfExpand"}),
+ 0, 0, 0, 973, 3, 0, 0, 0, 0, 0,
+ "rounds to an increment of hours"
+);
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, {smallestUnit: "minutes", roundingIncrement: 30, roundingMode: "halfExpand"}),
+ 0, 0, 0, 973, 4, 30, 0, 0, 0, 0,
+ "rounds to an increment of minutes"
+);
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, {smallestUnit: "seconds", roundingIncrement: 15, roundingMode: "halfExpand"}),
+ 0, 0, 0, 973, 4, 17, 0, 0, 0, 0,
+ "rounds to an increment of seconds"
+);
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, {smallestUnit: "milliseconds", roundingIncrement: 10, roundingMode: "halfExpand"}),
+ 0, 0, 0, 973, 4, 17, 4, 860, 0, 0,
+ "rounds to an increment of milliseconds"
+);
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, {smallestUnit: "microseconds", roundingIncrement: 10, roundingMode: "halfExpand"}),
+ 0, 0, 0, 973, 4, 17, 4, 864, 200, 0,
+ "rounds to an increment of microseconds"
+);
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, {smallestUnit: "nanoseconds", roundingIncrement: 10, roundingMode: "halfExpand"}),
+ 0, 0, 0, 973, 4, 17, 4, 864, 197, 530,
+ "rounds to an increment of nanoseconds"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-cleanly-divides.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-cleanly-divides.js
new file mode 100644
index 0000000000..d9ee3df308
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-cleanly-divides.js
@@ -0,0 +1,42 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Rounding increments that cleanly divide relevant units
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 321);
+
+[1, 2, 3, 4, 6, 8, 12].forEach((roundingIncrement) => {
+ const options = {smallestUnit: "hours", roundingIncrement};
+ assert(
+ earlier.until(later, options) instanceof Temporal.Duration,
+ `valid hour increments divide 24 (rounding increment = ${roundingIncrement})`
+ );
+});
+
+["minutes", "seconds"].forEach((smallestUnit) => {
+ [1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30].forEach((roundingIncrement) => {
+ const options = {smallestUnit, roundingIncrement};
+ assert(
+ earlier.until(later, options) instanceof Temporal.Duration,
+ `valid ${smallestUnit} increments divide 60 (rounding increment = ${roundingIncrement})`
+ );
+ });
+});
+
+["milliseconds", "microseconds", "nanoseconds"].forEach((smallestUnit) => {
+ [1, 2, 4, 5, 8, 10, 20, 25, 40, 50, 100, 125, 200, 250, 500].forEach((roundingIncrement) => {
+ const options = {smallestUnit, roundingIncrement};
+ assert(
+ earlier.until(later, options) instanceof Temporal.Duration,
+ `valid ${smallestUnit} increments divide 1000 (rounding increment = ${roundingIncrement}`
+ );
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-does-not-divide.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-does-not-divide.js
new file mode 100644
index 0000000000..92c300c98d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-does-not-divide.js
@@ -0,0 +1,48 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Rounding increments that do not cleanly divide the relevant unit
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 321);
+
+const nondivisibleUnits = {
+ "hours": 11,
+ "minutes": 29,
+ "seconds": 29,
+ "milliseconds": 29,
+ "microseconds": 29,
+ "nanoseconds": 29
+};
+
+Object.entries(nondivisibleUnits).forEach(([unit, increment]) => {
+ assert.throws(
+ RangeError,
+ () => earlier.until(later, {smallestUnit: unit, roundingIncrement: increment}),
+ `throws on increments that do not divide evenly into the next highest (unit = ${unit}, increment = ${increment})`
+ );
+});
+
+const equalDivisibleUnits = {
+ "hours": 24,
+ "minutes": 60,
+ "seconds": 60,
+ "milliseconds": 1000,
+ "microseconds": 1000,
+ "nanoseconds": 1000
+};
+
+Object.entries(equalDivisibleUnits).forEach(([unit, increment]) => {
+ assert.throws(
+ RangeError,
+ () => earlier.until(later, {smallestUnit: unit, roundingIncrement: increment}),
+ `throws on increments that are equal to the next highest (unit = ${unit}, rounding increment = ${increment})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-nan.js
new file mode 100644
index 0000000000..80f4f779a5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-nan.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: RangeError thrown when roundingIncrement option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaindatetime.prototype.until step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322);
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-non-integer.js
new file mode 100644
index 0000000000..859509878a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-non-integer.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Rounding for roundingIncrement option
+info: |
+ ToTemporalRoundingIncrement ( _normalizedOptions_ )
+
+ 1. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, *"number"*, *undefined*, *1*<sub>𝔽</sub>).
+ 2. If _increment_ is not finite, throw a *RangeError* exception.
+ 3. Let _integerIncrement_ be truncate(ℝ(_increment_)).
+ 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception.
+ 5. Return _integerIncrement_.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 5);
+const result = earlier.until(later, { roundingIncrement: 2.5, roundingMode: "trunc" });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, "roundingIncrement 2.5 truncates to 2");
+const result2 = earlier.until(later, { smallestUnit: "days", roundingIncrement: 1e9 + 0.5, roundingMode: "expand" });
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 1e9, 0, 0, 0, 0, 0, 0, "roundingIncrement 1e9 + 0.5 truncates to 1e9");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-out-of-range.js
new file mode 100644
index 0000000000..8b5bf55cda
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-out-of-range.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: RangeError thrown when roundingIncrement option out of range
+info: |
+ ToTemporalRoundingIncrement ( _normalizedOptions_ )
+
+ 1. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, *"number"*, *undefined*, *1*<sub>𝔽</sub>).
+ 2. If _increment_ is not finite, throw a *RangeError* exception.
+ 3. Let _integerIncrement_ be truncate(ℝ(_increment_)).
+ 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception.
+ 5. Return _integerIncrement_.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 5);
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -1 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 0 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 0.9 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 1e9 + 1 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: Infinity }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-undefined.js
new file mode 100644
index 0000000000..a93a4e6b36
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-undefined.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Fallback value for roundingIncrement option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaindatetime.prototype.until step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322);
+
+const explicit = earlier.until(later, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 397, 1, 1, 1, 1, 1, 1, "default roundingIncrement is 1");
+
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 397, 1, 1, 1, 1, 1, 1, "default roundingIncrement is 1");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-wrong-type.js
new file mode 100644
index 0000000000..7ac6ac6d1f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-wrong-type.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Type conversions for roundingIncrement option
+info: |
+ sec-getoption step 8.a:
+ a. Set _value_ to ? ToNumber(value).
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaindatetime.prototype.until step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => earlier.until(later, { roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 397, 1, 1, 1, 1, 1, 1, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 397, 1, 1, 1, 1, 1, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-ceil.js
new file mode 100644
index 0000000000..b5b23655c0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-ceil.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Tests calculations with roundingMode "ceil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [3], [-2]],
+ ["months", [0, 32], [0, -31]],
+ ["weeks", [0, 0, 140], [0, 0, -139]],
+ ["days", [0, 0, 0, 974], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 973, 5], [0, 0, 0, -973, -4]],
+ ["minutes", [0, 0, 0, 973, 4, 18], [0, 0, 0, -973, -4, -17]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 5], [0, 0, 0, -973, -4, -17, -4]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 865], [0, 0, 0, -973, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 198], [0, 0, 0, -973, -4, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "ceil";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-expand.js
new file mode 100644
index 0000000000..63eb3bba0c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-expand.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Tests calculations with roundingMode "expand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 140], [0, 0, -140]],
+ ["days", [0, 0, 0, 974], [0, 0, 0, -974]],
+ ["hours", [0, 0, 0, 973, 5], [0, 0, 0, -973, -5]],
+ ["minutes", [0, 0, 0, 973, 4, 18], [0, 0, 0, -973, -4, -18]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 5], [0, 0, 0, -973, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 865], [0, 0, 0, -973, -4, -17, -4, -865]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 198], [0, 0, 0, -973, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "expand";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-floor.js
new file mode 100644
index 0000000000..4fc41c1b12
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-floor.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Tests calculations with roundingMode "floor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [2], [-3]],
+ ["months", [0, 31], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -140]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -974]],
+ ["hours", [0, 0, 0, 973, 4], [0, 0, 0, -973, -5]],
+ ["minutes", [0, 0, 0, 973, 4, 17], [0, 0, 0, -973, -4, -18]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 4], [0, 0, 0, -973, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 864], [0, 0, 0, -973, -4, -17, -4, -865]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197], [0, 0, 0, -973, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "floor";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..c9e6d10c97
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfCeil.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Tests calculations with roundingMode "halfCeil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 973, 4], [0, 0, 0, -973, -4]],
+ ["minutes", [0, 0, 0, 973, 4, 17], [0, 0, 0, -973, -4, -17]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 5], [0, 0, 0, -973, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 864], [0, 0, 0, -973, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 198], [0, 0, 0, -973, -4, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "halfCeil";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfEven.js
new file mode 100644
index 0000000000..b8c1b5ee20
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfEven.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Tests calculations with roundingMode "halfEven".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 973, 4], [0, 0, 0, -973, -4]],
+ ["minutes", [0, 0, 0, 973, 4, 17], [0, 0, 0, -973, -4, -17]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 5], [0, 0, 0, -973, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 864], [0, 0, 0, -973, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 198], [0, 0, 0, -973, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "halfEven";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..1a833d6d0f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfExpand.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Tests calculations with roundingMode "halfExpand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 973, 4], [0, 0, 0, -973, -4]],
+ ["minutes", [0, 0, 0, 973, 4, 17], [0, 0, 0, -973, -4, -17]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 5], [0, 0, 0, -973, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 864], [0, 0, 0, -973, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 198], [0, 0, 0, -973, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "halfExpand";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..96d4eb34e5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfFloor.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Tests calculations with roundingMode "halfFloor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 973, 4], [0, 0, 0, -973, -4]],
+ ["minutes", [0, 0, 0, 973, 4, 17], [0, 0, 0, -973, -4, -17]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 5], [0, 0, 0, -973, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 864], [0, 0, 0, -973, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197], [0, 0, 0, -973, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "halfFloor";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..52011b2f13
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfTrunc.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Tests calculations with roundingMode "halfTrunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 973, 4], [0, 0, 0, -973, -4]],
+ ["minutes", [0, 0, 0, 973, 4, 17], [0, 0, 0, -973, -4, -17]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 5], [0, 0, 0, -973, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 864], [0, 0, 0, -973, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197], [0, 0, 0, -973, -4, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "halfTrunc";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfexpand-default-changes.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfexpand-default-changes.js
new file mode 100644
index 0000000000..72cde68fb8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfexpand-default-changes.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: A different default for largest unit will be used if smallest unit is larger than "days"
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 321);
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, {smallestUnit: "years", roundingMode: "halfExpand"}),
+ 3, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "assumes a different default for largestUnit if smallestUnit is larger than days (largest unit = years)"
+);
+TemporalHelpers.assertDuration(
+ earlier.until(later, {smallestUnit: "months", roundingMode: "halfExpand"}),
+ 0, 32, 0, 0, 0, 0, 0, 0, 0, 0,
+ "assumes a different default for largestUnit if smallestUnit is larger than days (largest unit = months)"
+);
+TemporalHelpers.assertDuration(
+ earlier.until(later, {smallestUnit: "weeks", roundingMode: "halfExpand"}),
+ 0, 0, 139, 0, 0, 0, 0, 0, 0, 0,
+ "assumes a different default for largestUnit if smallestUnit is larger than days (largest unit = weeks)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..667e88aaa0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-invalid-string.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 123, 987, 500);
+for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) {
+ assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "microsecond", roundingMode }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-trunc-is-default.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-trunc-is-default.js
new file mode 100644
index 0000000000..290e08cd04
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-trunc-is-default.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Show that truncation is the default rounding mode
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 321);
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, {smallestUnit: "minutes"}),
+ 0, 0, 0, 973, 4, 17,0, 0, 0, 0,
+ "trunc is the default (round up)"
+);
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, {smallestUnit: "seconds"}),
+ 0, 0, 0, 973, 4, 17, 4, 0, 0, 0,
+ "trunc is the default (round down)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-trunc.js
new file mode 100644
index 0000000000..6eb452ae4f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-trunc.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Tests calculations with roundingMode "trunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [2], [-2]],
+ ["months", [0, 31], [0, -31]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 973, 4], [0, 0, 0, -973, -4]],
+ ["minutes", [0, 0, 0, 973, 4, 17], [0, 0, 0, -973, -4, -17]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 4], [0, 0, 0, -973, -4, -17, -4]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 864], [0, 0, 0, -973, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197], [0, 0, 0, -973, -4, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "trunc";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-undefined.js
new file mode 100644
index 0000000000..3a25e93e3a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-undefined.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 123, 987, 500);
+
+const explicit1 = earlier.until(later, { smallestUnit: "microsecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 0, 0, 0, 1, 1, 1, 1, 123, 987, 0, "default roundingMode is trunc");
+const implicit1 = earlier.until(later, { smallestUnit: "microsecond" });
+TemporalHelpers.assertDuration(implicit1, 0, 0, 0, 1, 1, 1, 1, 123, 987, 0, "default roundingMode is trunc");
+
+const explicit2 = earlier.until(later, { smallestUnit: "millisecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 0, 0, 0, 1, 1, 1, 1, 123, 0, 0, "default roundingMode is trunc");
+const implicit2 = earlier.until(later, { smallestUnit: "millisecond" });
+TemporalHelpers.assertDuration(implicit2, 0, 0, 0, 1, 1, 1, 1, 123, 0, 0, "default roundingMode is trunc");
+
+const explicit3 = earlier.until(later, { smallestUnit: "second", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit3, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, "default roundingMode is trunc");
+const implicit3 = earlier.until(later, { smallestUnit: "second" });
+TemporalHelpers.assertDuration(implicit3, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, "default roundingMode is trunc");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..f8f12c6573
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-wrong-type.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => earlier.until(later, { smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 1, 1, 1, 1, 123, 987, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..93de6cef01
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-invalid-string.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 987, 654, 321);
+const badValues = [
+ "era",
+ "eraYear",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => earlier.until(later, { smallestUnit }),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..493b52af8b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-plurals-accepted.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 12, 13, 35, 57, 988, 655, 322);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => earlier.until(later, { smallestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-undefined.js
new file mode 100644
index 0000000000..884259ff1c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-undefined.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 987, 654, 321);
+
+const explicit = earlier.until(later, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 1, 1, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 1, 1, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..b646bd90bd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-wrong-type.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 987, 654, 321);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => earlier.until(later, { smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 1, 1, 1, 1, 987, 654, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/subseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/subseconds.js
new file mode 100644
index 0000000000..1bf72b6a69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/subseconds.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Returned granularity may be finer than seconds
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const feb20 = new Temporal.PlainDateTime(2020, 2, 1, 0, 0);
+const feb21 = new Temporal.PlainDateTime(2020, 2, 2, 0, 0, 0, 250, 250, 250);
+
+TemporalHelpers.assertDuration(
+ feb20.until(feb21, { largestUnit: "milliseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, 86400250, 250, 250,
+ "can return subseconds (millisecond precision)"
+);
+
+TemporalHelpers.assertDuration(
+ feb20.until(feb21, { largestUnit: "microseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, 0, 86400250250, 250,
+ "can return subseconds (microsecond precision)"
+);
+
+TemporalHelpers.assertDuration(
+ feb20.until(feb21, { largestUnit: "nanoseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 86400250250250,
+ "can return subseconds (nanosecond precision)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/units-changed.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/units-changed.js
new file mode 100644
index 0000000000..3da503c495
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/units-changed.js
@@ -0,0 +1,69 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Largest unit is respected
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const feb20 = new Temporal.PlainDateTime(2020, 2, 1, 0, 0);
+const feb21 = new Temporal.PlainDateTime(2021, 2, 1, 0, 0);
+
+TemporalHelpers.assertDuration(
+ feb20.until(feb21, { largestUnit: "years" }),
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "can return lower or higher units (years)"
+);
+
+TemporalHelpers.assertDuration(
+ feb20.until(feb21, { largestUnit: "months" }),
+ 0, 12, 0, 0, 0, 0, 0, 0, 0, 0,
+ "can return lower or higher units (months)"
+);
+
+TemporalHelpers.assertDuration(
+ feb20.until(feb21, { largestUnit: "weeks" }),
+ 0, 0, 52, 2, 0, 0, 0, 0, 0, 0,
+ "can return lower or higher units (weeks)"
+);
+
+TemporalHelpers.assertDuration(
+ feb20.until(feb21, { largestUnit: "hours" }),
+ 0, 0, 0, 0, 8784, 0, 0, 0, 0, 0,
+ "can return lower or higher units (hours)"
+);
+
+TemporalHelpers.assertDuration(
+ feb20.until(feb21, { largestUnit: "minutes" }),
+ 0, 0, 0, 0, 0, 527040, 0, 0, 0, 0,
+ "can return lower or higher units (minutes)"
+);
+
+TemporalHelpers.assertDuration(
+ feb20.until(feb21, { largestUnit: "seconds" }),
+ 0, 0, 0, 0, 0, 0, 31622400, 0, 0, 0,
+ "can return lower or higher units (seconds)"
+);
+
+TemporalHelpers.assertDuration(
+ feb20.until(feb21, { largestUnit: "milliseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, 31622400000, 0, 0,
+ "can return lower or higher units (milliseconds)"
+);
+
+TemporalHelpers.assertDuration(
+ feb20.until(feb21, { largestUnit: "microseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, 0, 31622400000000, 0,
+ "can return lower or higher units (microseconds)"
+);
+
+TemporalHelpers.assertDuration(
+ feb20.until(feb21, { largestUnit: "nanoseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 31622400000000000,
+ "can return lower or higher units (nanoseconds)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/weeks-months-mutually-exclusive.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/weeks-months-mutually-exclusive.js
new file mode 100644
index 0000000000..36acc146d4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/weeks-months-mutually-exclusive.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Weeks and months are mutually exclusive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+const laterDateTime = dt.add({ days: 42, hours: 3 });
+
+TemporalHelpers.assertDuration(
+ dt.until(laterDateTime, { largestUnit: "weeks" }),
+ 0, 0, 6, 0, 3, 0, 0, 0, 0, 0,
+ "weeks and months mutually exclusive (prefer weeks)"
+);
+
+TemporalHelpers.assertDuration(
+ dt.until(laterDateTime, { largestUnit: "months" }),
+ 0, 1, 0, 12, 3, 0, 0, 0, 0, 0,
+ "weeks and months mutually exclusive (prefer months)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/year-zero.js
new file mode 100644
index 0000000000..cd6f5f364a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/year-zero.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-12-07",
+ "-000000-12-07T03:24:30",
+ "-000000-12-07T03:24:30+01:00",
+ "-000000-12-07T03:24:30+00:00[UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/basic.js
new file mode 100644
index 0000000000..8cc67cdb3b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/basic.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.valueof
+description: Comparison operators (except !== and ===) do not work
+features: [Temporal]
+---*/
+
+const dt1 = new Temporal.PlainDateTime(1963, 2, 13, 9, 36, 29, 123, 456, 789);
+const dt1again = new Temporal.PlainDateTime(1963, 2, 13, 9, 36, 29, 123, 456, 789);
+const dt2 = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+
+assert.sameValue(dt1 === dt1, true, "object equality implies ===");
+assert.sameValue(dt1 !== dt1again, true, "object non-equality, even if all data is the same, implies !==");
+assert.throws(TypeError, () => dt1 < dt1, "< throws (same objects)");
+assert.throws(TypeError, () => dt1 < dt2, "< throws (different objects)");
+assert.throws(TypeError, () => dt1 > dt1, "> throws (same objects)");
+assert.throws(TypeError, () => dt1 > dt2, "> throws (different objects)");
+assert.throws(TypeError, () => dt1 <= dt1, "<= does not throw (same objects)");
+assert.throws(TypeError, () => dt1 <= dt2, "<= throws (different objects)");
+assert.throws(TypeError, () => dt1 >= dt1, ">= throws (same objects)");
+assert.throws(TypeError, () => dt1 >= dt2, ">= throws (different objects)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/branding.js
new file mode 100644
index 0000000000..7325efb87e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.valueof
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const valueOf = Temporal.PlainDateTime.prototype.valueOf;
+
+assert.sameValue(typeof valueOf, "function");
+
+assert.throws(TypeError, () => valueOf.call(undefined), "undefined");
+assert.throws(TypeError, () => valueOf.call(null), "null");
+assert.throws(TypeError, () => valueOf.call(true), "true");
+assert.throws(TypeError, () => valueOf.call(""), "empty string");
+assert.throws(TypeError, () => valueOf.call(Symbol()), "symbol");
+assert.throws(TypeError, () => valueOf.call(1), "1");
+assert.throws(TypeError, () => valueOf.call({}), "plain object");
+assert.throws(TypeError, () => valueOf.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => valueOf.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/builtin.js
new file mode 100644
index 0000000000..ebb2963ac2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.valueof
+description: >
+ Tests that Temporal.PlainDateTime.prototype.valueOf
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.valueOf),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.valueOf),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.valueOf),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.valueOf.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/length.js
new file mode 100644
index 0000000000..7c64b114ea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.valueof
+description: Temporal.PlainDateTime.prototype.valueOf.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.valueOf, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/name.js
new file mode 100644
index 0000000000..205c983cf8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.valueof
+description: Temporal.PlainDateTime.prototype.valueOf.name is "valueOf".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.valueOf, "name", {
+ value: "valueOf",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/not-a-constructor.js
new file mode 100644
index 0000000000..36a384a2c0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.valueof
+description: >
+ Temporal.PlainDateTime.prototype.valueOf does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.valueOf();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.valueOf), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.valueOf)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/prop-desc.js
new file mode 100644
index 0000000000..60ad86a0d2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.valueof
+description: The "valueOf" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.valueOf,
+ "function",
+ "`typeof PlainDateTime.prototype.valueOf` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "valueOf", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/basic.js
new file mode 100644
index 0000000000..9b1f195177
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/basic.js
@@ -0,0 +1,15 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.weekofyear
+description: Checking week of year for a "normal" case (non-undefined, non-boundary case, etc.)
+features: [Temporal]
+---*/
+
+const calendar = Temporal.Calendar.from("iso8601");
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar);
+assert.sameValue(datetime.weekOfYear, 47, "check week of year information");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/branding.js
new file mode 100644
index 0000000000..826226663e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.weekofyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const weekOfYear = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "weekOfYear").get;
+
+assert.sameValue(typeof weekOfYear, "function");
+
+assert.throws(TypeError, () => weekOfYear.call(undefined), "undefined");
+assert.throws(TypeError, () => weekOfYear.call(null), "null");
+assert.throws(TypeError, () => weekOfYear.call(true), "true");
+assert.throws(TypeError, () => weekOfYear.call(""), "empty string");
+assert.throws(TypeError, () => weekOfYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => weekOfYear.call(1), "1");
+assert.throws(TypeError, () => weekOfYear.call({}), "plain object");
+assert.throws(TypeError, () => weekOfYear.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => weekOfYear.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..53c69b52e1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.weekofyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const weekOfYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "weekOfYear");
+Object.defineProperty(Temporal.Calendar.prototype, "weekOfYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("weekOfYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.weekOfYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "weekOfYear", weekOfYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/custom.js
new file mode 100644
index 0000000000..f2f8584457
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.weekofyear
+description: Custom calendar tests for weekOfYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ weekOfYear(...args) {
+ ++calls;
+ assert.compareArray(args, [pdt], "weekOfYear arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pdt = new Temporal.PlainDateTime(1830, 8, 25, 20, 0, 0, 0, 0, 0, calendar);
+const result = pdt.weekOfYear;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/prop-desc.js
new file mode 100644
index 0000000000..269a04acb9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.weekofyear
+description: The "weekOfYear" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "weekOfYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/validate-calendar-value.js
new file mode 100644
index 0000000000..d1624e1617
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/validate-calendar-value.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.weekofyear
+description: Validate result returned from calendar weekOfYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ weekOfYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.throws(error, () => instance.weekOfYear, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/argument-not-object.js
new file mode 100644
index 0000000000..f0076b021f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/argument-not-object.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: Non-object arguments throw.
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const args = [
+ undefined,
+ null,
+ true,
+ "2020-01-12T10:20:30",
+ Symbol(),
+ 2020,
+ 2020n,
+];
+for (const argument of args) {
+ assert.throws(TypeError, () => instance.with(argument), `Does not support ${typeof argument}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/argument-object-insufficient-data.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/argument-object-insufficient-data.js
new file mode 100644
index 0000000000..c63204f4a5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/argument-object-insufficient-data.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Unrecognized properties (incl. plurals of recognized units) are ignored
+esid: sec-temporal.plaindatetime.prototype.with
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+assert.throws(
+ TypeError,
+ () => instance.with({}),
+ "empty object not acceptable"
+);
+
+const units = ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"];
+
+units.forEach((unit) => {
+ let plural = `${unit}s`;
+ let options = {};
+ options[plural] = 1;
+ assert.throws(
+ TypeError,
+ () => instance.with(options),
+ `plural unit ("${plural}" vs "${unit}") is not acceptable`
+ );
+});
+
+assert.throws(
+ TypeError,
+ () => instance.with({nonsense: true}),
+ "throw if no recognized properties present"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ instance.with({year: 1965, nonsense: true}),
+ 1965, 5, "M05", 2, 12, 34, 56, 987, 654, 321,
+ "unrecognized properties ignored & does not throw if recognized properties present)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/basic.js
new file mode 100644
index 0000000000..4d8893ceb1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/basic.js
@@ -0,0 +1,80 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: A variety of "normal" (non-throwing, non-boundary case, non-null, etc.) arguments
+esid: sec-temporal.plaindatetime.prototype.with
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+
+TemporalHelpers.assertPlainDateTime(
+ datetime.with({ year: 2019 }),
+ 2019, 11, "M11", 18, 15, 23, 30, 123, 456, 789,
+ "with year works"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ datetime.with({ month: 5 }),
+ 1976, 5, "M05", 18, 15, 23, 30, 123, 456, 789,
+ "with month works"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ datetime.with({ monthCode: "M05" }),
+ 1976, 5, "M05", 18, 15, 23, 30, 123, 456, 789,
+ "with month code works"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ datetime.with({ day: 5 }),
+ 1976, 11, "M11", 5, 15, 23, 30, 123, 456, 789,
+ "with day works"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ datetime.with({ hour: 5 }),
+ 1976, 11, "M11", 18, 5, 23, 30, 123, 456, 789,
+ "with hour works"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ datetime.with({ minute: 5 }),
+ 1976, 11, "M11", 18, 15, 5, 30, 123, 456, 789,
+ "with minute works"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ datetime.with({ second: 5 }),
+ 1976, 11, "M11", 18, 15, 23, 5, 123, 456, 789,
+ "with second works"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ datetime.with({ millisecond: 5 }),
+ 1976, 11, "M11", 18, 15, 23, 30, 5, 456, 789,
+ "with millisecond works"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ datetime.with({ microsecond: 5 }),
+ 1976, 11, "M11", 18, 15, 23, 30, 123, 5, 789,
+ "with microsecond works"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ datetime.with({ nanosecond: 5 }),
+ 1976, 11, "M11", 18, 15, 23, 30, 123, 456, 5,
+ "with nanosecond works"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ datetime.with({ month: 5, second: 15 }),
+ 1976, 5, "M05", 18, 15, 23, 15, 123, 456, 789,
+ "with month and second works"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/branding.js
new file mode 100644
index 0000000000..e658a4109b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const with_ = Temporal.PlainDateTime.prototype.with;
+
+assert.sameValue(typeof with_, "function");
+
+const args = [{ year: 2022 }];
+
+assert.throws(TypeError, () => with_.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => with_.apply(null, args), "null");
+assert.throws(TypeError, () => with_.apply(true, args), "true");
+assert.throws(TypeError, () => with_.apply("", args), "empty string");
+assert.throws(TypeError, () => with_.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => with_.apply(1, args), "1");
+assert.throws(TypeError, () => with_.apply({}, args), "plain object");
+assert.throws(TypeError, () => with_.apply(Temporal.PlainDateTime, args), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => with_.apply(Temporal.PlainDateTime.prototype, args), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..63191733a5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainDateTime(2023, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.with({ day: 5 });
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..5b28018cf0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,46 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const fieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "fields");
+Object.defineProperty(Temporal.Calendar.prototype, "fields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("fields should not be looked up");
+ },
+});
+const mergeFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "mergeFields");
+Object.defineProperty(Temporal.Calendar.prototype, "mergeFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("mergeFields should not be looked up");
+ },
+});
+const dateFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateFromFields should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.with({ year: 2001 });
+
+Object.defineProperty(Temporal.Calendar.prototype, "fields", fieldsOriginal);
+Object.defineProperty(Temporal.Calendar.prototype, "mergeFields", mergeFieldsOriginal);
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", dateFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/builtin.js
new file mode 100644
index 0000000000..4f4737af99
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: >
+ Tests that Temporal.PlainDateTime.prototype.with
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.with),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.with),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.with),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.with.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-fields-iterable.js
new file mode 100644
index 0000000000..26c0c8cc9d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-fields-iterable.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindatetime.prototype.with step 9:
+ 9. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+datetime.with({ year: 2005 });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..53b61f8a84
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+instance.with({ day: 24 });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-merge-fields-returns-primitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-merge-fields-returns-primitive.js
new file mode 100644
index 0000000000..805ed6e5ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-merge-fields-returns-primitive.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: >
+ with() should throw a TypeError if mergeFields() returns a primitive,
+ without passing the value on to any other calendar methods
+includes: [compareArray.js, temporalHelpers.js]
+features: [BigInt, Symbol, Temporal]
+---*/
+
+[undefined, null, true, 3.14159, "bad value", Symbol("no"), 7n].forEach((primitive) => {
+ const calendar = TemporalHelpers.calendarMergeFieldsReturnsPrimitive(primitive);
+ const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321, calendar);
+ assert.throws(TypeError, () => instance.with({ year: 2005 }), "bad return from mergeFields() throws");
+ assert.sameValue(calendar.dateFromFieldsCallCount, 0, "dateFromFields() never called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..295a391ef4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: >
+ Calendar.mergeFields method is called with null-prototype fields objects
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckMergeFieldsPrototypePollution();
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+instance.with({ day: 24 });
+assert.sameValue(calendar.mergeFieldsCallCount, 1, "mergeFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-options.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-options.js
new file mode 100644
index 0000000000..55f92aa702
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-options.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: >
+ The options argument is copied and the copy is passed to
+ Calendar#dateFromFields.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const options = {
+ extra: "property",
+};
+let calledDateFromFields = 0;
+class Calendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateFromFields(fields, optionsArg) {
+ ++calledDateFromFields;
+ assert.notSameValue(optionsArg, options, "should pass copied options object");
+ assert.sameValue(optionsArg.extra, "property", "should copy all properties from options object");
+ assert.sameValue(Object.getPrototypeOf(optionsArg), null, "Copy has null prototype");
+ return super.dateFromFields(fields, optionsArg);
+ }
+};
+const calendar = new Calendar();
+const plaindatetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+const result = plaindatetime.with({ year: 2005 }, options);
+TemporalHelpers.assertPlainDateTime(result, 2005, 5, "M05", 2, 12, 34, 56, 987, 654, 321);
+assert.sameValue(calledDateFromFields, 1, "should have called overridden dateFromFields once");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-temporal-object-throws.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-temporal-object-throws.js
new file mode 100644
index 0000000000..9e5eab90fb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-temporal-object-throws.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if a Temporal object with a calendar is supplied
+esid: sec-temporal.plaindatetime.prototype.with
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+
+const values = [
+ Temporal.PlainDate.from("2022-04-12"),
+ Temporal.PlainDateTime.from("2022-04-12T15:19:45"),
+ Temporal.PlainMonthDay.from("04-12"),
+ Temporal.PlainTime.from("15:19:45"),
+ Temporal.PlainYearMonth.from("2022-04"),
+ Temporal.ZonedDateTime.from("2022-04-12T15:19:45[UTC]"),
+];
+
+for (const value of values) {
+ Object.defineProperty(value, "calendar", {
+ get() { throw new Test262Error("should not get calendar property") }
+ });
+ Object.defineProperty(value, "timeZone", {
+ get() { throw new Test262Error("should not get timeZone property") }
+ });
+ assert.throws(
+ TypeError,
+ () => datetime.with(value),
+ "throws with temporal object"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-throws.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-throws.js
new file mode 100644
index 0000000000..9565cb370a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-throws.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if a calendar is supplied
+esid: sec-temporal.plaindatetime.prototype.with
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+
+assert.throws(
+ TypeError,
+ () => datetime.with({ year: 2021, calendar: "iso8601" }),
+ "throws with calendar property"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..35f27a64bf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const datetime = new Temporal.PlainDateTime(2023, 5, 1, 0, 0, 0, 0, 0, 0, calendar);
+
+assert.throws(RangeError, () => datetime.with({hour: 12}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/copies-merge-fields-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/copies-merge-fields-object.js
new file mode 100644
index 0000000000..a60773fc91
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/copies-merge-fields-object.js
@@ -0,0 +1,58 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: The object returned from mergeFields() is copied before being passed to dateFromFields().
+info: |
+ sec-temporal.plaindatetime.prototype.with steps 13–15:
+ 13. Set _fields_ to ? CalendarMergeFields(_calendar_, _fields_, _partialDate_).
+ 14. Set _fields_ to ? PrepareTemporalFields(_fields_, _fieldNames_, «»).
+ 15. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields step 2:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get day",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get microsecond",
+ "get microsecond.valueOf",
+ "call microsecond.valueOf",
+ "get millisecond",
+ "get millisecond.valueOf",
+ "call millisecond.valueOf",
+ "get minute",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get month",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get monthCode",
+ "get monthCode.toString",
+ "call monthCode.toString",
+ "get nanosecond",
+ "get nanosecond.valueOf",
+ "call nanosecond.valueOf",
+ "get second",
+ "get second.valueOf",
+ "call second.valueOf",
+ "get year",
+ "get year.valueOf",
+ "call year.valueOf",
+];
+
+const calendar = TemporalHelpers.calendarMergeFieldsGetters();
+const datetime = new Temporal.PlainDateTime(2021, 3, 31, 12, 34, 56, 987, 654, 321, calendar);
+datetime.with({ year: 2022 });
+
+assert.compareArray(calendar.mergeFieldsReturnOperations, expected, "getters called on mergeFields return");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/copy-properties-not-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/copy-properties-not-undefined.js
new file mode 100644
index 0000000000..60f627767f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/copy-properties-not-undefined.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: PreparePartialTemporalFields copies only defined properties of source object
+info: |
+ 4. For each value _property_ of _fieldNames_, do
+ a. Let _value_ be ? Get(_fields_, _property_).
+ b. If _value_ is not *undefined*, then
+ ...
+ iii. Perform ! CreateDataPropertyOrThrow(_result_, _property_, _value_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDateTime = new Temporal.PlainDateTime(2006, 1, 24, 11, 42, 58);
+
+TemporalHelpers.assertPlainDateTime(plainDateTime.with({ day: 8, hour: 10, year: undefined }),
+ 2006, 1, "M01", 8, 10, 42, 58, 0, 0, 0,
+ "only the properties that are present and defined in the plain object are copied"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..5be05fd288
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/duplicate-calendar-fields.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['monthCode'], ['day'], ['hour'], ['microsecond'], ['millisecond'], ['minute'], ['month'], ['nanosecond'], ['second'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const datetime = new Temporal.PlainDateTime(2023, 5, 1, 0, 0, 0, 0, 0, 0, calendar);
+
+ assert.throws(RangeError, () => datetime.with({hour: 12}));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..ae7ad4a2b4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/infinity-throws-rangeerror.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindatetime.prototype.with
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => instance.with({ [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.with({ [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/length.js
new file mode 100644
index 0000000000..539f00c49e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: Temporal.PlainDateTime.prototype.with.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.with, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/month-and-monthcode-must-agree.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/month-and-monthcode-must-agree.js
new file mode 100644
index 0000000000..f936006c14
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/month-and-monthcode-must-agree.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: The month and month code should agree
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+
+assert.throws(
+ RangeError,
+ () => datetime.with({ month: 5, monthCode: "M06" }),
+ "month and monthCode must agree"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/multiple-unrecognized-properties-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/multiple-unrecognized-properties-ignored.js
new file mode 100644
index 0000000000..9c9eb5c6dd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/multiple-unrecognized-properties-ignored.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Unrecognized units are ignored
+esid: sec-temporal.plaindatetime.prototype.with
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+const units = ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"];
+
+units.forEach((unit) => {
+ let plural = `${unit}s`;
+ let arg = { month: 12 };
+ arg[plural] = 1;
+ TemporalHelpers.assertPlainDateTime(
+ datetime.with(arg),
+ 1976, 12, "M12", 18, 15, 23, 30, 123, 456, 789,
+ `unrecognized property (${plural}) gets ignored`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/name.js
new file mode 100644
index 0000000000..a30c3f28e2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: Temporal.PlainDateTime.prototype.with.name is "with".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.with, "name", {
+ value: "with",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/not-a-constructor.js
new file mode 100644
index 0000000000..3ab26bd234
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: >
+ Temporal.PlainDateTime.prototype.with does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.with();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.with), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.with)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-empty.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-empty.js
new file mode 100644
index 0000000000..9ac3c21267
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-empty.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+
+TemporalHelpers.assertPlainDateTime(
+ datetime.with({ day: 40 }, {}),
+ 1976, 11, "M11", 30, 15, 23, 30, 123, 456, 789,
+ "options may be empty object"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ datetime.with({ day: 40 }, () => {}),
+ 1976, 11, "M11", 30, 15, 23, 30, 123, 456, 789,
+ "read empty options from function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-invalid.js
new file mode 100644
index 0000000000..2f945aeb50
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-invalid.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: Verify that undefined options are handled correctly.
+features: [Temporal, Symbol]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+
+const badOptions = [null, 1, 'hello', true, Symbol('foo'), 1n];
+
+badOptions.forEach((bad) => {
+ assert.throws(
+ TypeError,
+ () => datetime.with({ day: 5 }, bad),
+ `bad options (${typeof bad})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-undefined.js
new file mode 100644
index 0000000000..0b37d9c62a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-undefined.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 2, 2, 12, 34, 56, 987, 654, 321);
+const fields = { day: 31 };
+
+const explicit = datetime.with(fields, undefined);
+assert.sameValue(explicit.month, 2, "default overflow is constrain");
+assert.sameValue(explicit.day, 29, "default overflow is constrain");
+
+const implicit = datetime.with(fields);
+assert.sameValue(implicit.month, 2, "default overflow is constrain");
+assert.sameValue(implicit.day, 29, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-wrong-type.js
new file mode 100644
index 0000000000..0831b45561
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-wrong-type.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.with({ day: 5 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/order-of-operations.js
new file mode 100644
index 0000000000..a408ffd916
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/order-of-operations.js
@@ -0,0 +1,110 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: Properties on an object passed to with() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // RejectObjectWithCalendarOrTimeZone
+ "get fields.calendar",
+ "get fields.timeZone",
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ "getOwnPropertyDescriptor options.extra",
+ "get options.extra",
+ // lookup
+ "get this.calendar.dateFromFields",
+ "get this.calendar.fields",
+ "get this.calendar.mergeFields",
+ // CalendarFields
+ "call this.calendar.fields",
+ // PrepareTemporalFields on receiver
+ "get this.calendar.day",
+ "call this.calendar.day",
+ "get this.calendar.month",
+ "call this.calendar.month",
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ "get this.calendar.year",
+ "call this.calendar.year",
+ // PrepareTemporalFields on argument
+ "get fields.day",
+ "get fields.day.valueOf",
+ "call fields.day.valueOf",
+ "get fields.hour",
+ "get fields.hour.valueOf",
+ "call fields.hour.valueOf",
+ "get fields.microsecond",
+ "get fields.microsecond.valueOf",
+ "call fields.microsecond.valueOf",
+ "get fields.millisecond",
+ "get fields.millisecond.valueOf",
+ "call fields.millisecond.valueOf",
+ "get fields.minute",
+ "get fields.minute.valueOf",
+ "call fields.minute.valueOf",
+ "get fields.month",
+ "get fields.month.valueOf",
+ "call fields.month.valueOf",
+ "get fields.monthCode",
+ "get fields.monthCode.toString",
+ "call fields.monthCode.toString",
+ "get fields.nanosecond",
+ "get fields.nanosecond.valueOf",
+ "call fields.nanosecond.valueOf",
+ "get fields.second",
+ "get fields.second.valueOf",
+ "call fields.second.valueOf",
+ "get fields.year",
+ "get fields.year.valueOf",
+ "call fields.year.valueOf",
+ // CalendarMergeFields
+ "call this.calendar.mergeFields",
+ // InterpretTemporalDateTimeFields
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+ "call this.calendar.dateFromFields",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+TemporalHelpers.observeProperty(actual, instance, "hour", 12, "this");
+TemporalHelpers.observeProperty(actual, instance, "minute", 34, "this");
+TemporalHelpers.observeProperty(actual, instance, "second", 56, "this");
+TemporalHelpers.observeProperty(actual, instance, "millisecond", 987, "this");
+TemporalHelpers.observeProperty(actual, instance, "microsecond", 654, "this");
+TemporalHelpers.observeProperty(actual, instance, "nanosecond", 321, "this");
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ year: 1.7,
+ month: 1.7,
+ monthCode: "M01",
+ day: 1.7,
+ hour: 1.7,
+ minute: 1.7,
+ second: 1.7,
+ millisecond: 1.7,
+ microsecond: 1.7,
+ nanosecond: 1.7,
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ overflow: "constrain",
+ extra: "property",
+}, "options");
+
+instance.with(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/overflow-invalid-string.js
new file mode 100644
index 0000000000..750d7be7e0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/overflow-invalid-string.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindatetime.prototype.with step 16:
+ 16. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12);
+
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => datetime.with({ minute: 45 }, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/overflow-undefined.js
new file mode 100644
index 0000000000..32cf45f248
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/overflow-undefined.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindatetime.prototype.with step 16:
+ 16. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12);
+const explicit = datetime.with({ minute: 67 }, { overflow: undefined });
+TemporalHelpers.assertPlainDateTime(explicit, 2000, 5, "M05", 2, 12, 59, 0, 0, 0, 0, "default overflow is constrain");
+const implicit = datetime.with({ minute: 67 }, {});
+TemporalHelpers.assertPlainDateTime(implicit, 2000, 5, "M05", 2, 12, 59, 0, 0, 0, 0, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/overflow-wrong-type.js
new file mode 100644
index 0000000000..6af0e9768a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/overflow-wrong-type.js
@@ -0,0 +1,48 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindatetime.prototype.with step 16:
+ 16. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12);
+
+// See TemporalHelpers.checkStringOptionWrongType(); this code path has
+// different expectations for observable calls
+
+assert.throws(RangeError, () => datetime.with({ minute: 45 }, { overflow: null }), "null");
+assert.throws(RangeError, () => datetime.with({ minute: 45 }, { overflow: true }), "true");
+assert.throws(RangeError, () => datetime.with({ minute: 45 }, { overflow: false }), "false");
+assert.throws(TypeError, () => datetime.with({ minute: 45 }, { overflow: Symbol() }), "symbol");
+assert.throws(RangeError, () => datetime.with({ minute: 45 }, { overflow: 2 }), "number");
+assert.throws(RangeError, () => datetime.with({ minute: 45 }, { overflow: 2n }), "bigint");
+assert.throws(RangeError, () => datetime.with({ minute: 45 }, { overflow: {} }), "plain object");
+
+// toString property should only be read and converted to a string once, because
+// a copied object with the resulting string on it is passed to
+// Calendar.dateFromFields().
+const expected = [
+ "get overflow.toString",
+ "call overflow.toString",
+];
+const actual = [];
+const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow");
+const result = datetime.with({ minute: 45 }, { overflow: observer });
+TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 45, 0, 0, 0, 0, "object with toString");
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/prop-desc.js
new file mode 100644
index 0000000000..bbbe9d6d15
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: The "with" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.with,
+ "function",
+ "`typeof PlainDateTime.prototype.with` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "with", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..6bfd62f345
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const datetime = new Temporal.PlainDateTime(2023, 5, 1, 0, 0, 0, 0, 0, 0, calendar);
+
+assert.throws(RangeError, () => datetime.with({hour: 12}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/read-time-fields-before-datefromfields.js
new file mode 100644
index 0000000000..9fc273cdfb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/read-time-fields-before-datefromfields.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.plaindatetime.prototype.with step 15:
+ 15. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const datetime = new Temporal.PlainDateTime(2021, 3, 31, 12, 34, 56, 987, 654, 321, calendar);
+const newDatetime = datetime.with({ year: 2022 });
+
+assert.sameValue(newDatetime.hour, 12, "hour value");
+assert.sameValue(newDatetime.minute, 34, "minute value");
+assert.sameValue(newDatetime.second, 56, "second value");
+assert.sameValue(newDatetime.millisecond, 987, "millisecond value");
+assert.sameValue(newDatetime.microsecond, 654, "microsecond value");
+assert.sameValue(newDatetime.nanosecond, 321, "nanosecond value");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/string-throws.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/string-throws.js
new file mode 100644
index 0000000000..f6908a4cca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/string-throws.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if a string argument is supplied
+esid: sec-temporal.plaindatetime.prototype.with
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const baddies = ["12:00", "1995-04-07", "2019-05-17T12:34:56.007007007", "2019-05-17T12:34:56.007007007Z", "42"];
+
+baddies.forEach((bad) => {
+ assert.throws(
+ TypeError,
+ () => instance.with(bad),
+ `bad argument (${bad})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/subclassing-ignored.js
new file mode 100644
index 0000000000..b06990e6bd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/subclassing-ignored.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: Objects of a subclass are never created as return values for with()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDateTime,
+ [2000, 5, 2, 12, 34, 56, 987, 654, 321],
+ "with",
+ [{ nanosecond: 1 }],
+ (result) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 1),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/timezone-throws.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/timezone-throws.js
new file mode 100644
index 0000000000..3460a16758
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/timezone-throws.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if a timezone is supplied
+esid: sec-temporal.plaindatetime.prototype.with
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+
+assert.throws(
+ TypeError,
+ () => datetime.with({ year: 2021, timeZone: "UTC" }),
+ "throws with timezone property"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/argument-string.js
new file mode 100644
index 0000000000..e9e1586477
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/argument-string.js
@@ -0,0 +1,57 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withcalendar
+description: String argument, if it names a recognizable calendar, gets cast
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const calendar = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "something special",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ toString() { return "something special"; },
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar);
+const result = dt.withCalendar("iso8601");
+
+TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 15, 23, 30, 123, 456, 789,
+ "'iso8601' is a recognizable calendar"
+);
+
+assert.sameValue(
+ result.getISOFields().calendar,
+ "iso8601",
+ "underlying calendar has changed and calendar slot stores a string"
+);
+
+assert.throws(
+ RangeError,
+ () => dt.withCalendar("this will fail"),
+ "unknown calendar throws"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/basic.js
new file mode 100644
index 0000000000..1509be6e07
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/basic.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withcalendar
+description: Non-throwing non-edge case
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+const calendar = new Temporal.Calendar("iso8601");
+
+const result = dt.withCalendar(calendar);
+
+TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 15, 23, 30, 123, 456, 789,
+ "works"
+);
+
+assert.sameValue(result.getCalendar(), calendar, "underlying calendar is unchanged");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/branding.js
new file mode 100644
index 0000000000..a888613ab4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withcalendar
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const withCalendar = Temporal.PlainDateTime.prototype.withCalendar;
+
+assert.sameValue(typeof withCalendar, "function");
+
+const args = [new Temporal.Calendar("iso8601")];
+
+assert.throws(TypeError, () => withCalendar.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => withCalendar.apply(null, args), "null");
+assert.throws(TypeError, () => withCalendar.apply(true, args), "true");
+assert.throws(TypeError, () => withCalendar.apply("", args), "empty string");
+assert.throws(TypeError, () => withCalendar.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => withCalendar.apply(1, args), "1");
+assert.throws(TypeError, () => withCalendar.apply({}, args), "plain object");
+assert.throws(TypeError, () => withCalendar.apply(Temporal.PlainDateTime, args), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => withCalendar.apply(Temporal.PlainDateTime.prototype, args), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..004403b09a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withcalendar
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.withCalendar("iso8601");
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/builtin.js
new file mode 100644
index 0000000000..49f64fd2fc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withcalendar
+description: >
+ Tests that Temporal.PlainDateTime.prototype.withCalendar
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.withCalendar),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.withCalendar),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.withCalendar),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.withCalendar.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-case-insensitive.js
new file mode 100644
index 0000000000..90f3905fff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-case-insensitive.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withcalendar
+description: Calendar names are case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+
+const arg = "iSo8601";
+const result = instance.withCalendar(arg);
+assert.sameValue(result.calendarId, "iso8601", "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-number.js
new file mode 100644
index 0000000000..34afdcd997
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-number.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withcalendar
+description: A number is not allowed to be a calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+
+const numbers = [
+ 1,
+ -19761118,
+ 19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.withCalendar(arg),
+ "A number is not a valid ISO string for Calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-string-leap-second.js
new file mode 100644
index 0000000000..bd62a3a13c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-string-leap-second.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withcalendar
+description: Leap second is a valid ISO string for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+
+const arg = "2016-12-31T23:59:60";
+const result = instance.withCalendar(arg);
+assert.sameValue(
+ result.calendarId,
+ "iso8601",
+ "leap second is a valid ISO string for Calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-string.js
new file mode 100644
index 0000000000..c8018bde0d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-string.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withcalendar
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+
+const arg = "iso8601";
+
+const result = instance.withCalendar(arg);
+assert.sameValue(result.getISOFields().calendar, "iso8601", `Calendar created from string "${arg}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-temporal-object.js
new file mode 100644
index 0000000000..fdabe3e22c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-temporal-object.js
@@ -0,0 +1,64 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withcalendar
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal-totemporalcalendar step 1.b:
+ b. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2000, 5, 2);
+const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const plainMonthDay = new Temporal.PlainMonthDay(5, 2);
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 5);
+const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+[plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((arg) => {
+ const actual = [];
+ const expected = [];
+
+ const calendar = arg.getISOFields().calendar;
+
+ Object.defineProperty(arg, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+ const result = instance.withCalendar(arg);
+ assert.sameValue(result.getISOFields().calendar, calendar, "Temporal object coerced to calendar");
+
+ assert.compareArray(actual, expected, "calendar getter not called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-wrong-type.js
new file mode 100644
index 0000000000..ff7d515fbf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-wrong-type.js
@@ -0,0 +1,64 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withcalendar
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for Calendar
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => instance.withCalendar(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.withCalendar(arg), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/length.js
new file mode 100644
index 0000000000..73d506d949
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withcalendar
+description: Temporal.PlainDateTime.prototype.withCalendar.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.withCalendar, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/missing-argument.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/missing-argument.js
new file mode 100644
index 0000000000..502f7a9f48
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/missing-argument.js
@@ -0,0 +1,15 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withcalendar
+description: TypeError thrown when calendar argument not given
+features: [Temporal]
+---*/
+
+const plainDateTime = Temporal.PlainDateTime.from("1976-11-18T14:00:00");
+assert.throws(TypeError, () => plainDateTime.withCalendar(), "missing argument");
+assert.throws(TypeError, () => plainDateTime.withCalendar(undefined), "undefined argument");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/name.js
new file mode 100644
index 0000000000..1d9d820482
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withcalendar
+description: Temporal.PlainDateTime.prototype.withCalendar.name is "withCalendar".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.withCalendar, "name", {
+ value: "withCalendar",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/not-a-constructor.js
new file mode 100644
index 0000000000..fd1c779874
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withcalendar
+description: >
+ Temporal.PlainDateTime.prototype.withCalendar does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.withCalendar();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.withCalendar), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.withCalendar)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/prop-desc.js
new file mode 100644
index 0000000000..9059757976
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withcalendar
+description: The "withCalendar" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.withCalendar,
+ "function",
+ "`typeof PlainDateTime.prototype.withCalendar` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "withCalendar", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/subclassing-ignored.js
new file mode 100644
index 0000000000..f8670228b6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/subclassing-ignored.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withcalendar
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const customCalendar = {
+ era() { return undefined; },
+ eraYear() { return undefined; },
+ year() { return 1900; },
+ month() { return 2; },
+ monthCode() { return "M02"; },
+ day() { return 5; },
+ id: "custom-calendar",
+ toString() { return "custom-calendar"; },
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDateTime,
+ [2000, 5, 2, 12, 34, 56, 987, 654, 321],
+ "withCalendar",
+ [customCalendar],
+ (result) => {
+ TemporalHelpers.assertPlainDateTime(result, 1900, 2, "M02", 5, 12, 34, 56, 987, 654, 321);
+ assert.sameValue(result.getCalendar(), customCalendar, "calendar result");
+ },
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..aff156fa39
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.withPlainDate(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..f070f616c2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-calendar-datefromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.withPlainDate(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..7db65ba3b4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-constructor-in-calendar-fields.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+assert.throws(RangeError, () => instance.withPlainDate(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..094d3b1764
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+ assert.throws(RangeError, () => instance.withPlainDate(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-leap-second.js
new file mode 100644
index 0000000000..cd45b2dbdf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-leap-second.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: Leap second is a valid ISO string for PlainDate
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.withPlainDate(arg);
+TemporalHelpers.assertPlainDateTime(
+ result1,
+ 2016, 12, "M12", 31, 12, 34, 56, 987, 654, 321,
+ "leap second is a valid ISO string for PlainDate"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.withPlainDate(arg);
+TemporalHelpers.assertPlainDateTime(
+ result2,
+ 2016, 12, "M12", 31, 12, 34, 56, 987, 654, 321,
+ "second: 60 is ignored in property bag for PlainDate"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-number.js
new file mode 100644
index 0000000000..48bbc1cf50
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-number.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.withPlainDate(arg),
+ 'Numbers cannot be used in place of an ISO string for PlainDate'
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-object-insufficient-data.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-object-insufficient-data.js
new file mode 100644
index 0000000000..e7d7859da5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-object-insufficient-data.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: Unrecognized properties of plain object ignored
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1995, 12, 7, 3, 24, 30);
+
+assert.throws(
+ TypeError,
+ () => dt.withPlainDate({}),
+ "empty object not acceptable"
+);
+
+assert.throws(
+ TypeError,
+ () => dt.withPlainDate({ months: 12 }), // should be "month"
+ "no recognized properties (look like it might work)"
+);
+
+assert.throws(
+ TypeError,
+ () => dt.with({nonsense: true}),
+ "no recognized properties (clearly won't work)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.withPlainDate({ year: 2000, month: 6, day: 1, months: 123 }), // 'months' unrecognized; see above
+ 2000, 6, "M06", 1, 3, 24, 30, 0, 0, 0,
+ "unrecognized properties ignored & does not throw if recognized properties present)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-object.js
new file mode 100644
index 0000000000..7e7bbddf1c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-object.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: Plain object may be acceptable
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1995, 12, 7, 3, 24, 30);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.withPlainDate({ year: 2000, month: 6, day: 1 }),
+ 2000, 6, "M06", 1, 3, 24, 30, 0, 0, 0,
+ "plain object works"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar-noniso.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar-noniso.js
new file mode 100644
index 0000000000..1255bf51c1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar-noniso.js
@@ -0,0 +1,56 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: PlainDate calendar is preserved with ISO PDT
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const cal = {
+ id: 'thisisnotiso',
+ era() { return undefined; },
+ eraYear() { return undefined; },
+ toString() { return "this is a string"; },
+ year() { return 2008; },
+ month() { return 9; },
+ monthCode() { return "M09"; },
+ day() { return 6; },
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const pdt = new Temporal.PlainDateTime(1995, 12, 7, 3, 24, 30, 0, 0, 0);
+assert.sameValue(pdt.calendarId, "iso8601", "PlainDateTime with ISO calendar");
+const pd = new Temporal.PlainDate(2010, 11, 12, cal);
+const shifted = pdt.withPlainDate(pd);
+
+TemporalHelpers.assertPlainDateTime(
+ shifted,
+ 2008, 9, "M09", 6, 3, 24, 30, 0, 0, 0,
+ "calendar is changed if receiver has ISO calendar (1)"
+ // Testing of era and eraYear should only be coded under intl402
+);
+
+assert.sameValue(
+ shifted.getCalendar(),
+ cal,
+ "calendar is changed if receiver has ISO calendar (2)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar-same-id.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar-same-id.js
new file mode 100644
index 0000000000..3ef31c8d45
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar-same-id.js
@@ -0,0 +1,79 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: PlainDate calendar is preserved when both calendars have the same id
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const cal1 = {
+ id: "this is a string",
+ toString() { return "this is another string"; },
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const cal2 = {
+ id: "this is a string",
+ era() { return undefined; },
+ eraYear() { return undefined; },
+ toString() { return "thisisnotiso"; },
+ year() { return 2008; },
+ month() { return 9; },
+ monthCode() { return "M09"; },
+ day() { return 6; },
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const pdt = new Temporal.PlainDateTime(1995, 12, 7, 3, 24, 30, 0, 0, 0, cal1);
+const pd = new Temporal.PlainDate(2010, 11, 12, cal2);
+const shifted = pdt.withPlainDate(pd);
+
+TemporalHelpers.assertPlainDateTime(
+ shifted,
+ 2008, 9, "M09", 6, 3, 24, 30, 0, 0, 0,
+ "calendar is changed with same id (1)"
+ // Testing of era and eraYear should only be coded under intl402
+);
+
+assert.sameValue(
+ shifted.getCalendar(),
+ cal2,
+ "calendar is changed with same id (2)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar-same-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar-same-object.js
new file mode 100644
index 0000000000..7c36ca99e2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar-same-object.js
@@ -0,0 +1,60 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: PlainDate calendar is preserved when both calendars are the same object
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+let calls = 0;
+const cal = {
+ id: 'thisisnotiso',
+ era() { return undefined; },
+ eraYear() { return undefined; },
+ toString() {
+ ++calls;
+ return "this is a string";
+ },
+ year() { return 2008; },
+ month() { return 9; },
+ monthCode() { return "M09"; },
+ day() { return 6; },
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const pdt = new Temporal.PlainDateTime(1995, 12, 7, 3, 24, 30, 0, 0, 0, cal);
+const pd = new Temporal.PlainDate(2010, 11, 12, cal);
+const shifted = pdt.withPlainDate(pd);
+
+TemporalHelpers.assertPlainDateTime(
+ shifted,
+ 2008, 9, "M09", 6, 3, 24, 30, 0, 0, 0,
+ "calendar is unchanged with same calendars (1)"
+ // Testing of era and eraYear should only be coded under intl402
+);
+
+assert.sameValue(
+ shifted.getCalendar(),
+ cal,
+ "calendar is unchanged with same calendars (2)"
+);
+assert.sameValue(calls, 0, "should not have called cal.toString()");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar.js
new file mode 100644
index 0000000000..d85612f889
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar.js
@@ -0,0 +1,56 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: Original PDT calendar is preserved with ISO PlainDate
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const cal = {
+ id: 'thisisnotiso',
+ era() { return undefined; },
+ eraYear() { return undefined; },
+ toString() { return "this is a string"; },
+ year() { return 2008; },
+ month() { return 9; },
+ monthCode() { return "M09"; },
+ day() { return 6; },
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const pdt = new Temporal.PlainDateTime(1995, 12, 7, 3, 24, 30, 0, 0, 0, cal);
+const pd = new Temporal.PlainDate(2010, 11, 12);
+assert.sameValue(pd.calendarId, "iso8601", "PlainDate with ISO calendar");
+const shifted = pdt.withPlainDate(pd);
+
+TemporalHelpers.assertPlainDateTime(
+ shifted,
+ 2008, 9, "M09", 6, 3, 24, 30, 0, 0, 0,
+ "calendar is unchanged if input has ISO calendar (1)"
+ // Testing of era and eraYear should only be coded under intl402
+);
+
+assert.sameValue(
+ shifted.getCalendar(),
+ cal,
+ "calendar is unchanged if input has ISO calendar (2)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate.js
new file mode 100644
index 0000000000..fb713898c1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: PlainDate object is acceptable
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1995, 12, 7, 3, 24, 30);
+const date = new Temporal.PlainDate(2020, 1, 23);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.withPlainDate(date),
+ 2020, 1, "M01", 23, 3, 24, 30, 0, 0, 0,
+ "PlainDate argument works"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindatetime.js
new file mode 100644
index 0000000000..377c7724a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindatetime.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.withplaindate
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainDate by reading internal slots
+info: |
+ sec-temporal.plaindatetime.prototype.withplaindate step 3:
+ 3. Let _plainDate_ be ? ToTemporalDate(_plainDateLike_).
+ sec-temporal-totemporaldate step 2.b:
+ b. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ i. Return ! CreateTemporalDate(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const receiver = new Temporal.PlainDateTime(2001, 9, 9, 6, 54, 32, 123, 456, 789);
+ const result = receiver.withPlainDate(datetime);
+ TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 6, 54, 32, 123, 456, 789);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..a5d1fd08e0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: The calendar name is case-insensitive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.withPlainDate(arg);
+TemporalHelpers.assertPlainDateTime(result, 1976, 11, "M11", 18, 12, 34, 56, 987, 654, 321, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..ac91345475
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: Leap second is a valid ISO string for a calendar in a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.withPlainDate(arg);
+TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 12, 34, 56, 987, 654, 321,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..cbd1950207
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-number.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.withPlainDate(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..c8a2dcbcd9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-string.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.withPlainDate(arg);
+TemporalHelpers.assertPlainDateTime(result, 1976, 11, "M11", 18, 12, 34, 56, 987, 654, 321, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..0baf544ed1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.withPlainDate(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.withPlainDate(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..d39551d616
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-year-zero.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainDate(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..8753275d77
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-proto-in-calendar-fields.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+assert.throws(RangeError, () => instance.withPlainDate(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..e15d9e970d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-calendar-annotation.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.withPlainDate(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 15, 23, 0, 0, 0, 0,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..34d98195f3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!foo=bar]",
+ "1970-01-01T00:00[!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainDate(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..53e460dac6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-date-with-utc-offset.js
@@ -0,0 +1,49 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.withPlainDate(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 15, 23, 0, 0, 0, 0,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+const invalidStrings = [
+ "2022-09-15Z",
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainDate(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-invalid.js
new file mode 100644
index 0000000000..d7d5f1c03c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-invalid.js
@@ -0,0 +1,64 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainDate(arg),
+ `"${arg}" should not be a valid ISO string for a PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-iso-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-iso-calendar.js
new file mode 100644
index 0000000000..4f253733bc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-iso-calendar.js
@@ -0,0 +1,54 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: Original PDT calendar is preserved with ISO string
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const cal = {
+ id: "thisisnotiso",
+ era() { return undefined; },
+ eraYear() { return undefined; },
+ toString() { return "this is a string"; },
+ year() { return 2008; },
+ month() { return 9; },
+ monthCode() { return "M09"; },
+ day() { return 6; },
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const dt = new Temporal.PlainDateTime(1995, 12, 7, 3, 24, 30, 0, 0, 0, cal);
+const shifted = dt.withPlainDate("2010-11-12");
+
+TemporalHelpers.assertPlainDateTime(
+ shifted,
+ 2008, 9, "M09", 6, 3, 24, 30, 0, 0, 0,
+ "calendar is unchanged if input has ISO calendar (1)"
+ // Testing of era and eraYear should only be coded under intl402
+);
+
+assert.sameValue(
+ shifted.getCalendar(),
+ cal,
+ "calendar is unchanged if input has ISO calendar (2)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..e864fc5bea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-multiple-calendar.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainDate(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..28492d2479
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-multiple-time-zone.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[UTC][UTC]",
+ "1970-01-01T00:00[UTC][UTC]",
+ "1970-01-01T00:00[!UTC][UTC]",
+ "1970-01-01T00:00[UTC][!UTC]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00[UTC][foo=bar][UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainDate(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-time-separators.js
new file mode 100644
index 0000000000..cdfb3c1f58
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-time-separators.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.withPlainDate(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 15, 23, 0, 0, 0, 0,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..6a110024d9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-time-zone-annotation.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[Asia/Kolkata]", "named, with no time"],
+ ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"],
+ ["2000-05-02[+00:00]", "numeric, with no time"],
+ ["2000-05-02[!-02:30]", "numeric, with ! and no time"],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.withPlainDate(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 15, 23, 0, 0, 0, 0,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..642a911fec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-unknown-annotation.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.withPlainDate(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 15, 23, 0, 0, 0, 0,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..c0a04dcdde
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-with-utc-designator.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainDate(arg),
+ "String with UTC designator should not be valid as a PlainDate"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string.js
new file mode 100644
index 0000000000..bea0f65186
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: PlainDate-like string argument is acceptable
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const dt = new Temporal.PlainDateTime(1995, 12, 7, 3, 24, 30);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.withPlainDate("2018-09-15"),
+ 2018, 9, "M09", 15, 3, 24, 30, 0, 0, 0,
+ "PlainDate-like string argument works"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-wrong-type.js
new file mode 100644
index 0000000000..06f30c6762
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-wrong-type.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => instance.withPlainDate(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.withPlainDate(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-convert.js
new file mode 100644
index 0000000000..233705b50c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+assert.throws(Test262Error, () => instance.withPlainDate(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..cab1b1db13
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-slots.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+instance.withPlainDate(arg);
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..6eeedcd1b0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.withPlainDate(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..63fe5656ec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => plain.withPlainDate(zoned),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..d18bb10db3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.withPlainDate(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..191d49e8e4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => plain.withPlainDate(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/branding.js
new file mode 100644
index 0000000000..754f29a7a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const withPlainDate = Temporal.PlainDateTime.prototype.withPlainDate;
+
+assert.sameValue(typeof withPlainDate, "function");
+
+const args = [new Temporal.PlainDate(2022, 6, 22)];
+
+assert.throws(TypeError, () => withPlainDate.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => withPlainDate.apply(null, args), "null");
+assert.throws(TypeError, () => withPlainDate.apply(true, args), "true");
+assert.throws(TypeError, () => withPlainDate.apply("", args), "empty string");
+assert.throws(TypeError, () => withPlainDate.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => withPlainDate.apply(1, args), "1");
+assert.throws(TypeError, () => withPlainDate.apply({}, args), "plain object");
+assert.throws(TypeError, () => withPlainDate.apply(Temporal.PlainDateTime, args), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => withPlainDate.apply(Temporal.PlainDateTime.prototype, args), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..5b78381f1c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.withPlainDate(new Temporal.PlainDate(2001, 6, 13));
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/builtin.js
new file mode 100644
index 0000000000..260cb95caa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: >
+ Tests that Temporal.PlainDateTime.prototype.withPlainDate
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.withPlainDate),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.withPlainDate),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.withPlainDate),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.withPlainDate.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..34b0955ac4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-datefromfields-called-with-options-undefined.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+instance.withPlainDate({ year: 2000, month: 5, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-fields-iterable.js
new file mode 100644
index 0000000000..a53cf9d14c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-fields-iterable.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindatetime.prototype.withplaindate step 3:
+ 3. Let _plainDate_ be ? ToTemporalDate(_plainDateLike_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.PlainDateTime(2000, 5, 3, 13, 3, 27, 123, 456, 789, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+datetime.withPlainDate({ year: 2001, month: 6, day: 4, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-temporal-object.js
new file mode 100644
index 0000000000..ecc32638c5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-temporal-object.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindatetime.prototype.withplaindate step 3:
+ 3. Let _plainDate_ be ? ToTemporalDate(_plainDateLike_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => {
+ const datetime = new Temporal.PlainDateTime(2000, 5, 3, 13, 3, 27, 123, 456, 789);
+ // the PlainDate's calendar will override the PlainDateTime's ISO calendar
+ const result = datetime.withPlainDate({ year: 2001, month: 6, day: 4, calendar: temporalObject });
+ assert.sameValue(result.getCalendar(), calendar, "Temporal object coerced to calendar");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..934b4207c9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/infinity-throws-rangeerror.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15);
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.withPlainDate({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.withPlainDate({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/length.js
new file mode 100644
index 0000000000..72a684301c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: Temporal.PlainDateTime.prototype.withPlainDate.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.withPlainDate, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/name.js
new file mode 100644
index 0000000000..4e528f6cd5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: Temporal.PlainDateTime.prototype.withPlainDate.name is "withPlainDate".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.withPlainDate, "name", {
+ value: "withPlainDate",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/non-compatible-calendars-throw.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/non-compatible-calendars-throw.js
new file mode 100644
index 0000000000..3b7f15afb4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/non-compatible-calendars-throw.js
@@ -0,0 +1,56 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.withplaindate
+description: If two non-ISO calendars are involved, an error is raised
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const cal = {
+ id: "foo",
+ toString() { return "this is a string"; },
+ ...calendarMethods,
+};
+
+const dt = new Temporal.PlainDateTime(1995, 12, 7, 3, 24, 30, 0, 0, 0, cal);
+
+const anotherCal = {
+ id: "bar",
+ toString() { return "this is another string"; },
+ ...calendarMethods,
+};
+
+const date = new Temporal.PlainDate(2008, 9, 6, anotherCal);
+
+assert.throws(
+ RangeError,
+ () => dt.withPlainDate(date),
+ "throws if both `this` and `other` have a non-ISO calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/not-a-constructor.js
new file mode 100644
index 0000000000..35bb31258b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: >
+ Temporal.PlainDateTime.prototype.withPlainDate does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.withPlainDate();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.withPlainDate), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.withPlainDate)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/prop-desc.js
new file mode 100644
index 0000000000..4da47c13b6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: The "withPlainDate" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.withPlainDate,
+ "function",
+ "`typeof PlainDateTime.prototype.withPlainDate` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "withPlainDate", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/subclassing-ignored.js
new file mode 100644
index 0000000000..686f5b7e44
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/subclassing-ignored.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDateTime,
+ [2000, 5, 2, 12, 34, 56, 987, 654, 321],
+ "withPlainDate",
+ ["1999-04-27"],
+ (result) => TemporalHelpers.assertPlainDateTime(result, 1999, 4, "M04", 27, 12, 34, 56, 987, 654, 321),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/year-zero.js
new file mode 100644
index 0000000000..7b8342376b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/year-zero.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainDate(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-number.js
new file mode 100644
index 0000000000..c57bc4aa1f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-number.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: A number is invalid in place of an ISO string for Temporal.PlainTime
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+
+const numbers = [
+ 1,
+ -123456.987654321,
+ 1234567,
+ 123456.9876543219,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.withPlainTime(arg),
+ `A number (${arg}) is not a valid ISO string for PlainTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-object-insufficient-data.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-object-insufficient-data.js
new file mode 100644
index 0000000000..a0532e47d4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-object-insufficient-data.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: A plain object can be used as an argument
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const dt = new Temporal.PlainDateTime(2015, 12, 7, 3, 24, 30, 0, 3, 500);
+
+assert.throws(
+ TypeError,
+ () => dt.withPlainTime({}),
+ "empty object not an acceptable argument"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.withPlainTime({ hour: 10 }),
+ 2015, 12, "M12", 7, 10, 0, 0, 0, 0, 0,
+ "plain object (hour) works"
+);
+
+assert.throws(
+ TypeError,
+ () => dt.withPlainTime({ hours: 9 }), // should be "hour", see above
+ "plain object with a single unrecognized property fails"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.withPlainTime({ hour: 10, seconds: 123 }),
+ 2015, 12, "M12", 7, 10, 0, 0, 0, 0, 0,
+ "unrecognized properties are ignored if at least one recognized property is present"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..5c4d6b028f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-calendar-annotation.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["12:34:56.987654321[u-ca=iso8601]", "without time zone"],
+ ["12:34:56.987654321[UTC][u-ca=iso8601]", "with time zone"],
+ ["12:34:56.987654321[!u-ca=iso8601]", "with ! and no time zone"],
+ ["12:34:56.987654321[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["T12:34:56.987654321[u-ca=iso8601]", "with T and no time zone"],
+ ["T12:34:56.987654321[UTC][u-ca=iso8601]", "with T and time zone"],
+ ["T12:34:56.987654321[!u-ca=iso8601]", "with T, !, and no time zone"],
+ ["T12:34:56.987654321[UTC][!u-ca=iso8601]", "with T, !, and time zone"],
+ ["1970-01-01T12:34:56.987654321[u-ca=iso8601]", "with date and no time zone"],
+ ["1970-01-01T12:34:56.987654321[UTC][u-ca=iso8601]", "with date and time zone"],
+ ["1970-01-01T12:34:56.987654321[!u-ca=iso8601]", "with !, date, and no time zone"],
+ ["1970-01-01T12:34:56.987654321[UTC][!u-ca=iso8601]", "with !, date, and time zone"],
+ ["12:34:56.987654321[u-ca=hebrew]", "calendar annotation ignored"],
+ ["12:34:56.987654321[u-ca=unknown]", "calendar annotation ignored even if unknown calendar"],
+ ["12:34:56.987654321[!u-ca=unknown]", "calendar annotation ignored even if unknown calendar with !"],
+ ["1970-01-01T12:34:56.987654321[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.withPlainTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 12, 34, 56, 987, 654, 321,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..f0535a69b0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "00:00[!foo=bar]",
+ "T00:00[!foo=bar]",
+ "1970-01-01T00:00[!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainTime(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..eb14fdd13b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-date-with-utc-offset.js
@@ -0,0 +1,53 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+const validStrings = [
+ "12:34:56.987654321+00:00",
+ "12:34:56.987654321+00:00[UTC]",
+ "12:34:56.987654321+00:00[!UTC]",
+ "12:34:56.987654321-02:30[America/St_Johns]",
+ "1976-11-18T12:34:56.987654321+00:00",
+ "1976-11-18T12:34:56.987654321+00:00[UTC]",
+ "1976-11-18T12:34:56.987654321+00:00[!UTC]",
+ "1976-11-18T12:34:56.987654321-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.withPlainTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 12, 34, 56, 987, 654, 321,
+ `"${arg}" is a valid UTC offset with time for PlainTime`
+ );
+}
+
+const invalidStrings = [
+ "2022-09-15Z",
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainTime(arg),
+ `"${arg}" UTC offset without time is not valid for PlainTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..c1c58dd36c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-multiple-calendar.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainTime(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..b50a240a48
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-multiple-time-zone.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "00:00[UTC][UTC]",
+ "T00:00[UTC][UTC]",
+ "1970-01-01T00:00[UTC][UTC]",
+ "1970-01-01T00:00[!UTC][UTC]",
+ "1970-01-01T00:00[UTC][!UTC]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00[UTC][foo=bar][UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainTime(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-no-implicit-midnight.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-no-implicit-midnight.js
new file mode 100644
index 0000000000..288d13416c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-no-implicit-midnight.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: RangeError thrown if a date-only string is passed in a PlainTime context
+features: [Temporal, arrow-function]
+---*/
+
+const arg = "2019-10-01";
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+assert.throws(
+ RangeError,
+ () => instance.withPlainTime(arg),
+ "Date-only string throws, does not implicitly convert to midnight"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-time-designator-required-for-disambiguation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-time-designator-required-for-disambiguation.js
new file mode 100644
index 0000000000..2fbe0e86ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-time-designator-required-for-disambiguation.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: ISO 8601 time designator "T" required in cases of ambiguity
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+TemporalHelpers.ISO.plainTimeStringsAmbiguous().forEach((string) => {
+ let arg = string;
+ assert.throws(
+ RangeError,
+ () => instance.withPlainTime(arg),
+ `'${arg}' is ambiguous and requires T prefix`
+ );
+ // The same string with a T prefix should not throw:
+ arg = `T${string}`;
+ instance.withPlainTime(arg);
+
+ arg = ` ${string}`;
+ assert.throws(
+ RangeError,
+ () => instance.withPlainTime(arg),
+ `space is not accepted as a substitute for T prefix: '${arg}'`
+ );
+});
+
+// None of these should throw without a T prefix, because they are unambiguously time strings:
+TemporalHelpers.ISO.plainTimeStringsUnambiguous().forEach(
+ (arg) => instance.withPlainTime(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-time-separators.js
new file mode 100644
index 0000000000..b7ff3060b6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-time-separators.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T12:34:56.987654321", "uppercase T"],
+ ["1976-11-18t12:34:56.987654321", "lowercase T"],
+ ["1976-11-18 12:34:56.987654321", "space between date and time"],
+ ["T12:34:56.987654321", "time-only uppercase T"],
+ ["t12:34:56.987654321", "time-only lowercase T"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.withPlainTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 12, 34, 56, 987, 654, 321,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..ac21c47797
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-time-zone-annotation.js
@@ -0,0 +1,51 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["12:34:56.987654321[Asia/Kolkata]", "named, with no offset"],
+ ["12:34:56.987654321[!Europe/Vienna]", "named, with ! and no offset"],
+ ["12:34:56.987654321[+00:00]", "numeric, with no offset"],
+ ["12:34:56.987654321[!-02:30]", "numeric, with ! and no offset"],
+ ["T12:34:56.987654321[UTC]", "named, with T and no offset"],
+ ["T12:34:56.987654321[!Africa/Abidjan]", "named, with T, !, and no offset"],
+ ["T12:34:56.987654321[+01:00]", "numeric, with T and no offset"],
+ ["T12:34:56.987654321[!-08:00]", "numeric, with T, !, and no offset"],
+ ["12:34:56.987654321+00:00[America/Sao_Paulo]", "named, with offset"],
+ ["12:34:56.987654321+00:00[!Asia/Tokyo]", "named, with ! and offset"],
+ ["12:34:56.987654321+00:00[-02:30]", "numeric, with offset"],
+ ["12:34:56.987654321+00:00[!+00:00]", "numeric, with ! and offset"],
+ ["T12:34:56.987654321+00:00[America/New_York]", "named, with T and offset"],
+ ["T12:34:56.987654321+00:00[!UTC]", "named, with T, !, and offset"],
+ ["T12:34:56.987654321+00:00[-08:00]", "numeric, with T and offset"],
+ ["T12:34:56.987654321+00:00[!+01:00]", "numeric, with T, !, and offset"],
+ ["1970-01-01T12:34:56.987654321[Africa/Lagos]", "named, with date and no offset"],
+ ["1970-01-01T12:34:56.987654321[!America/Vancouver]", "named, with date, !, and no offset"],
+ ["1970-01-01T12:34:56.987654321[+00:00]", "numeric, with date and no offset"],
+ ["1970-01-01T12:34:56.987654321[!-02:30]", "numeric, with date, !, and no offset"],
+ ["1970-01-01T12:34:56.987654321+00:00[Europe/London]", "named, with date and offset"],
+ ["1970-01-01T12:34:56.987654321+00:00[!Asia/Seoul]", "named, with date, offset, and !"],
+ ["1970-01-01T12:34:56.987654321+00:00[+01:00]", "numeric, with date and offset"],
+ ["1970-01-01T12:34:56.987654321+00:00[!-08:00]", "numeric, with date, offset, and !"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.withPlainTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 12, 34, 56, 987, 654, 321,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..837d16ed8e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-unknown-annotation.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["12:34:56.987654321[foo=bar]", "alone"],
+ ["12:34:56.987654321[UTC][foo=bar]", "with time zone"],
+ ["12:34:56.987654321[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["T12:34:56.987654321[foo=bar]", "with T"],
+ ["T12:34:56.987654321[UTC][foo=bar]", "with T and time zone"],
+ ["T12:34:56.987654321[u-ca=iso8601][foo=bar]", "with T and calendar"],
+ ["T12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with T, time zone, and calendar"],
+ ["1970-01-01T12:34:56.987654321[foo=bar]", "with date"],
+ ["1970-01-01T12:34:56.987654321[UTC][foo=bar]", "with date and time zone"],
+ ["1970-01-01T12:34:56.987654321[u-ca=iso8601][foo=bar]", "with date and calendar"],
+ ["1970-01-01T12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with date, time zone, and calendar"],
+ ["1970-01-01T12:34:56.987654321[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.withPlainTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 12, 34, 56, 987, 654, 321,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-with-time-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-with-time-designator.js
new file mode 100644
index 0000000000..53f6f8533c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-with-time-designator.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: ISO 8601 time designator "T" allowed at the start of PlainTime strings
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 1, 1, 12, 30, 45, 123, 456, 789);
+const validStrings = [
+ "T00:30",
+ "t00:30",
+ "T0030",
+ "t0030",
+ "T00:30:00",
+ "t00:30:00",
+ "T003000",
+ "t003000",
+ "T00:30:00.000000000",
+ "t00:30:00.000000000",
+ "T003000.000000000",
+ "t003000.000000000",
+];
+validStrings.forEach((arg) => {
+ const result = instance.withPlainTime(arg);
+ TemporalHelpers.assertPlainDateTime(result, 2000, 1, "M01", 1, 0, 30, 0, 0, 0, 0, `T prefix is accepted: ${arg}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..60ed3676fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-with-utc-designator.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: RangeError thrown if a string with UTC designator is used as a PlainTime
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+ "09:00:00Z[UTC]",
+ "09:00:00Z",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainTime(arg),
+ "String with UTC designator should not be valid as a PlainTime"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-without-time-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-without-time-designator.js
new file mode 100644
index 0000000000..2d0af7c731
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-without-time-designator.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: String argument without ISO 8601 time designator "T" allowed
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const dt = new Temporal.PlainDateTime(2015, 12, 7, 3, 24, 30, 0, 3, 500);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.withPlainTime("12:34"),
+ 2015, 12, "M12", 7, 12, 34, 0, 0, 0, 0,
+ "time-like string works"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-time.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-time.js
new file mode 100644
index 0000000000..51ede37ea3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-time.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: An instance of PlainTime can be used as an argument
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const dt = new Temporal.PlainDateTime(2015, 12, 7, 3, 24, 30, 0, 3, 500);
+const hour = 11;
+const minute = 22;
+const time = new Temporal.PlainTime(hour, minute);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.withPlainTime(time),
+ 2015,
+ 12,
+ "M12",
+ 7,
+ hour,
+ minute,
+ 0,
+ 0,
+ 0,
+ 0,
+ "PlainTime argument works"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-wrong-type.js
new file mode 100644
index 0000000000..3ce1146118
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-wrong-type.js
@@ -0,0 +1,42 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainTime
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => instance.withPlainTime(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainTime, "Temporal.PlainTime, object"],
+ [Temporal.PlainTime.prototype, "Temporal.PlainTime.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.withPlainTime(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 0000000000..3fb5f18f0a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,47 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: Negative time fields are balanced upwards if the argument is given as ZonedDateTime
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-temporal-totemporaltime step 3.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ ii. 1. Set _plainDateTime_ to ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
+ sec-temporal.plaindatetime.prototype.withplaintime step 4:
+ 4. Let _plainTime_ be ? ToTemporalTime(_plainTimeLike_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// This code path is encountered if the time zone offset is negative and its
+// absolute value in nanoseconds is greater than the nanosecond field of the
+// exact time's epoch parts
+const tz = TemporalHelpers.specificOffsetTimeZone(-2);
+const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
+
+const pdt = new Temporal.PlainDateTime(2000, 5, 2);
+const newpdt = pdt.withPlainTime(datetime);
+
+TemporalHelpers.assertPlainDateTime(newpdt, 2000, 5, "M05", 2, 1, 1, 1, 1, 0, 999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..90134da1be
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+const result = instance.withPlainTime(datetime);
+TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 16, 50, 35, 0, 0, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..e96dca9640
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.withPlainTime(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..e916235f4c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => plain.withPlainTime(zoned),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..60d258eda4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.withPlainTime(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..54d4aabd9b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => plain.withPlainTime(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/branding.js
new file mode 100644
index 0000000000..cae7cd48bf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const withPlainTime = Temporal.PlainDateTime.prototype.withPlainTime;
+
+assert.sameValue(typeof withPlainTime, "function");
+
+assert.throws(TypeError, () => withPlainTime.call(undefined), "undefined");
+assert.throws(TypeError, () => withPlainTime.call(null), "null");
+assert.throws(TypeError, () => withPlainTime.call(true), "true");
+assert.throws(TypeError, () => withPlainTime.call(""), "empty string");
+assert.throws(TypeError, () => withPlainTime.call(Symbol()), "symbol");
+assert.throws(TypeError, () => withPlainTime.call(1), "1");
+assert.throws(TypeError, () => withPlainTime.call({}), "plain object");
+assert.throws(TypeError, () => withPlainTime.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => withPlainTime.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/builtin.js
new file mode 100644
index 0000000000..3b4d77779a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: >
+ Tests that Temporal.PlainDateTime.prototype.withPlainTime
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.withPlainTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.withPlainTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.withPlainTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.withPlainTime.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/leap-second.js
new file mode 100644
index 0000000000..27bab2e006
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/leap-second.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: Leap second is a valid ISO string for PlainTime
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.withPlainTime(arg);
+TemporalHelpers.assertPlainDateTime(
+ result1,
+ 2000, 5, "M05", 2, 23, 59, 59, 0, 0, 0,
+ "leap second is a valid ISO string for PlainTime"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.withPlainTime(arg);
+TemporalHelpers.assertPlainDateTime(
+ result2,
+ 2000, 5, "M05", 2, 23, 59, 59, 0, 0, 0,
+ "second: 60 is ignored in property bag for PlainTime"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/length.js
new file mode 100644
index 0000000000..d090f2385c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: Temporal.PlainDateTime.prototype.withPlainTime.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.withPlainTime, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/name.js
new file mode 100644
index 0000000000..ed1b0a27c4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: Temporal.PlainDateTime.prototype.withPlainTime.name is "withPlainTime".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.withPlainTime, "name", {
+ value: "withPlainTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/no-argument-default-to-midnight.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/no-argument-default-to-midnight.js
new file mode 100644
index 0000000000..a291dc154a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/no-argument-default-to-midnight.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: If no argument is given, default to midnight
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(2015, 12, 7, 3, 24, 30, 0, 3, 500);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.withPlainTime(),
+ 2015, 12, "M12", 7, 0, 0, 0, 0, 0, 0,
+ "no argument defaults to midnight"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/not-a-constructor.js
new file mode 100644
index 0000000000..f0568aef06
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: >
+ Temporal.PlainDateTime.prototype.withPlainTime does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.withPlainTime();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.withPlainTime), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.withPlainTime)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/plaintime-propertybag-no-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/plaintime-propertybag-no-time-units.js
new file mode 100644
index 0000000000..a074797a02
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/plaintime-propertybag-no-time-units.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: Missing time units in property bag default to 0
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 1, 1, 12, 30, 45, 123, 456, 789);
+
+const props = {};
+assert.throws(TypeError, () => instance.withPlainTime(props), "TypeError if no properties are present");
+
+props.minute = 30;
+const result = instance.withPlainTime(props);
+TemporalHelpers.assertPlainDateTime(result, 2000, 1, "M01", 1, 0, 30, 0, 0, 0, 0, "missing time units default to 0");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/prop-desc.js
new file mode 100644
index 0000000000..f874bcd013
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: The "withPlainTime" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.withPlainTime,
+ "function",
+ "`typeof PlainDateTime.prototype.withPlainTime` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "withPlainTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/subclassing-ignored.js
new file mode 100644
index 0000000000..a28b2cfc9e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/subclassing-ignored.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDateTime,
+ [2000, 5, 2, 12, 34, 56, 987, 654, 321],
+ "withPlainTime",
+ ["05:43:21.123456789"],
+ (result) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 5, 43, 21, 123, 456, 789),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/time-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/time-undefined.js
new file mode 100644
index 0000000000..929ea523ea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/time-undefined.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: The time is assumed to be midnight if not given
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const explicit = datetime.withPlainTime(undefined);
+assert.sameValue(explicit.hour, 0, "default time is midnight");
+assert.sameValue(explicit.minute, 0, "default time is midnight");
+assert.sameValue(explicit.second, 0, "default time is midnight");
+assert.sameValue(explicit.millisecond, 0, "default time is midnight");
+assert.sameValue(explicit.microsecond, 0, "default time is midnight");
+assert.sameValue(explicit.nanosecond, 0, "default time is midnight");
+
+const implicit = datetime.withPlainTime();
+assert.sameValue(implicit.hour, 0, "default time is midnight");
+assert.sameValue(implicit.minute, 0, "default time is midnight");
+assert.sameValue(implicit.second, 0, "default time is midnight");
+assert.sameValue(implicit.millisecond, 0, "default time is midnight");
+assert.sameValue(implicit.microsecond, 0, "default time is midnight");
+assert.sameValue(implicit.nanosecond, 0, "default time is midnight");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/year-zero.js
new file mode 100644
index 0000000000..6be051a45f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/year-zero.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-12-07T03:24:30",
+ "-000000-12-07T03:24:30+01:00",
+ "-000000-12-07T03:24:30+00:00[UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainTime(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/branding.js
new file mode 100644
index 0000000000..71d9222d6d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.year
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const year = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "year").get;
+
+assert.sameValue(typeof year, "function");
+
+assert.throws(TypeError, () => year.call(undefined), "undefined");
+assert.throws(TypeError, () => year.call(null), "null");
+assert.throws(TypeError, () => year.call(true), "true");
+assert.throws(TypeError, () => year.call(""), "empty string");
+assert.throws(TypeError, () => year.call(Symbol()), "symbol");
+assert.throws(TypeError, () => year.call(1), "1");
+assert.throws(TypeError, () => year.call({}), "plain object");
+assert.throws(TypeError, () => year.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => year.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..8fc12c64f9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.year
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const yearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "year");
+Object.defineProperty(Temporal.Calendar.prototype, "year", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("year should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.year;
+
+Object.defineProperty(Temporal.Calendar.prototype, "year", yearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/custom.js
new file mode 100644
index 0000000000..f2dfc106bc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.year
+description: Custom calendar tests for year().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ year(...args) {
+ ++calls;
+ assert.compareArray(args, [pdt], "year arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pdt = new Temporal.PlainDateTime(1830, 8, 25, 20, 0, 0, 0, 0, 0, calendar);
+const result = pdt.year;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/prop-desc.js
new file mode 100644
index 0000000000..79ae1af772
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.year
+description: The "year" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "year");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/validate-calendar-value.js
new file mode 100644
index 0000000000..1d35fb8597
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/validate-calendar-value.js
@@ -0,0 +1,54 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.year
+description: Validate result returned from calendar year() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [NaN, RangeError],
+ ["string", TypeError],
+ [{}, TypeError],
+ [null, TypeError],
+ [true, TypeError],
+ [false, TypeError],
+ [7.1, RangeError],
+ [-0.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ year() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.throws(error, () => instance.year, `${typeof result} ${String(result)} not converted to integer`);
+});
+
+const preservedResults = [
+ -7,
+];
+
+preservedResults.forEach(result => {
+ const calendar = new class extends Temporal.Calendar {
+ year() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.sameValue(instance.year, result, `${typeof result} ${String(result)} preserved`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/basic.js
new file mode 100644
index 0000000000..7f11b118e0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/basic.js
@@ -0,0 +1,15 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.yearofweek
+description: Checking yearOfWeek for a "normal" case (non-undefined, non-boundary case, etc.)
+features: [Temporal]
+---*/
+
+const calendar = Temporal.Calendar.from("iso8601");
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar);
+assert.sameValue(datetime.yearOfWeek, 1976, "check yearOfWeek information");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/branding.js
new file mode 100644
index 0000000000..01584442eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.yearofweek
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const yearOfWeek = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "yearOfWeek").get;
+
+assert.sameValue(typeof yearOfWeek, "function");
+
+assert.throws(TypeError, () => yearOfWeek.call(undefined), "undefined");
+assert.throws(TypeError, () => yearOfWeek.call(null), "null");
+assert.throws(TypeError, () => yearOfWeek.call(true), "true");
+assert.throws(TypeError, () => yearOfWeek.call(""), "empty string");
+assert.throws(TypeError, () => yearOfWeek.call(Symbol()), "symbol");
+assert.throws(TypeError, () => yearOfWeek.call(1), "1");
+assert.throws(TypeError, () => yearOfWeek.call({}), "plain object");
+assert.throws(TypeError, () => yearOfWeek.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => yearOfWeek.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..ac90671ed2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.yearofweek
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const yearOfWeekOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "yearOfWeek");
+Object.defineProperty(Temporal.Calendar.prototype, "yearOfWeek", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("yearOfWeek should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.yearOfWeek;
+
+Object.defineProperty(Temporal.Calendar.prototype, "yearOfWeek", yearOfWeekOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/custom.js
new file mode 100644
index 0000000000..2bd3306412
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.yearofweek
+description: Custom calendar tests for yearOfWeek().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ yearOfWeek(...args) {
+ ++calls;
+ assert.compareArray(args, [pdt], "yearOfWeek arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pdt = new Temporal.PlainDateTime(1830, 8, 25, 20, 0, 0, 0, 0, 0, calendar);
+const result = pdt.yearOfWeek;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/prop-desc.js
new file mode 100644
index 0000000000..43b57c137a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.yearofweek
+description: The "yearOfWeek" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "yearOfWeek");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/validate-calendar-value.js
new file mode 100644
index 0000000000..cd43fd4474
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/validate-calendar-value.js
@@ -0,0 +1,54 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.yearofweek
+description: Validate result returned from calendar yearOfWeek() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [NaN, RangeError],
+ ["string", TypeError],
+ [{}, TypeError],
+ [null, TypeError],
+ [true, TypeError],
+ [false, TypeError],
+ [7.1, RangeError],
+ [-0.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ yearOfWeek() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.throws(error, () => instance.yearOfWeek, `${typeof result} ${String(result)} not converted to integer`);
+});
+
+const preservedResults = [
+ -7,
+];
+
+preservedResults.forEach(result => {
+ const calendar = new class extends Temporal.Calendar {
+ yearOfWeek() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.sameValue(instance.yearOfWeek, result, `${typeof result} ${String(result)} preserved`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/second-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/second-undefined.js
new file mode 100644
index 0000000000..6a827ccf6c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/second-undefined.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Second argument defaults to 0 if not given
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const args = [2000, 5, 2, 12, 34];
+
+TemporalHelpers.assertPlainDateTime(
+ new Temporal.PlainDateTime(...args, undefined),
+ 2000, 5, "M05", 2, 12, 34, 0, 0, 0, 0,
+ "second default argument (argument present)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ new Temporal.PlainDateTime(...args),
+ 2000, 5, "M05", 2, 12, 34, 0, 0, 0, 0,
+ "second default argument (argument missing)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/subclass.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/subclass.js
new file mode 100644
index 0000000000..d0d1991b31
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/subclass.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Test for Temporal.PlainDateTime subclassing.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomPlainDateTime extends Temporal.PlainDateTime {
+}
+
+const instance = new CustomPlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+TemporalHelpers.assertPlainDateTime(instance, 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 321);
+assert.sameValue(Object.getPrototypeOf(instance), CustomPlainDateTime.prototype, "Instance of CustomPlainDateTime");
+assert(instance instanceof CustomPlainDateTime, "Instance of CustomPlainDateTime");
+assert(instance instanceof Temporal.PlainDateTime, "Instance of Temporal.PlainDateTime");
+
+reportCompare(0, 0);