summaryrefslogtreecommitdiffstats
path: root/js/src/tests/test262/built-ins/Temporal/PlainDate
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/PlainDate
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/PlainDate')
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/argument-convert.js55
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/argument-invalid.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/basic.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/builtin.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-case-insensitive.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-number.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-temporal-object.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-undefined.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-wrong-type.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-calendar-datefromfields-called-with-null-prototype-fields.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-number.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-object.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-plaindatetime.js53
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-case-insensitive.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-leap-second.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-number.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-string.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-wrong-type.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-year-zero.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-calendar-annotation.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-critical-unknown-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-date-with-utc-offset.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-invalid.js69
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-multiple-calendar.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-multiple-time-zone.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-time-separators.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-time-zone-annotation.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-unknown-annotation.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-with-utc-designator.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-wrong-type.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/basic.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/builtin.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar-datefromfields-called-with-options-undefined.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar-fields-iterable.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar-temporal-object.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/exhaustive.js71
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/infinity-throws-rangeerror.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/leap-second.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/not-a-constructor.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/use-internal-slots.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/year-zero.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/constructor.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-calendar-datefromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-duplicate-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-leap-second.js44
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-number.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-object-invalid.js44
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-object-valid.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-plaindate.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-plaindatetime.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-case-insensitive.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-leap-second.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-number.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-calendar-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-critical-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-date-with-utc-offset.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-invalid.js63
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-time-separators.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-time-zone-annotation.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-trailing-junk.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-unknown-annotation.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-with-utc-designator.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-wrong-type.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-slots.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/builtin.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/calendar-fields-custom.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/calendar-fields-iterable.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/infinity-throws-rangeerror.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/limits.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/not-a-constructor.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/observable-get-overflow-argument-primitive.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/observable-get-overflow-argument-string-invalid.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-wrong-type.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/order-of-operations.js79
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/overflow-invalid-string.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/overflow-undefined.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/overflow-wrong-type.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/subclassing-ignored.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/infinity-throws-rangeerror.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/limits.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/missing-arguments.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/negative-infinity-throws-rangeerror.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-duration-max.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-duration-out-of-range.js75
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-invalid-property.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-mixed-sign.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-not-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-singular-properties.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-string-negative-fractional-units.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/balance-smaller-units-basic.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/balance-smaller-units.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/basic.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/calendar-invalid-return.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/custom.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/limits.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/negative-infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/non-integer-throws-rangeerror.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/options-undefined.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/order-of-operations.js129
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-constrain.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-invalid-string.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-reject.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-undefined.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/constructor.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/basic.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/basic.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/basic.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/basic.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/basic.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-object-invalid.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-object-valid.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-plaindatetime.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-case-insensitive.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-leap-second.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-number.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-calendar-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-critical-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-date-with-utc-offset.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-invalid.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-time-separators.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-time-zone-annotation.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/basic.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-call-different.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-call-same.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-datefromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-fields-iterable.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-no-call.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/custom.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/field-names.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/field-prop-desc.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/field-traversal-order.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/prototype.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/basic.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/validate-calendar-value.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/validate-calendar-value.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/basic.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/prop-desc.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-leap-second.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-plaindatetime.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-case-insensitive.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-leap-second.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-string.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-calendar-annotation.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-critical-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-date-with-utc-offset.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-invalid.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-time-separators.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-time-zone-annotation.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-unknown-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/basic.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-dateadd-called-with-plaindate-instance.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-datefromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-fields-iterable.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-id-match.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-invalid-return.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-mismatch.js62
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/custom.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/days-in-month.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/days-in-year.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-default.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-higher-units.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-invalid-string.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-plurals-accepted.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-smallestunit-mismatch.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/order-of-operations.js213
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/round-cross-unit-boundary.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/rounding-relative.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/rounding-zero-year-month-week-length.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-nan.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-non-integer.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-out-of-range.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-undefined.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-ceil.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-expand.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-floor.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfCeil.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfEven.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfExpand.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfFloor.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfTrunc.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-invalid-string.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-trunc.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-undefined.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-higher-units.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-invalid-string.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-plurals-accepted.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/weeks-months.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-duration-max.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-duration-out-of-range.js75
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-invalid-property.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-mixed-sign.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-not-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-singular-properties.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-string-negative-fractional-units.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/balance-smaller-units-basic.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/balance-smaller-units.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/basic.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/calendar-invalid-return.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/custom.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/limits.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/negative-infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/non-integer-throws-rangeerror.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/options-undefined.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/order-of-operations.js129
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-constrain.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-invalid-string.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-reject.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-undefined.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/basic.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/year-format.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/return-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-calendar-annotation.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-critical-unknown-annotation.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-date-with-utc-offset.js53
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-multiple-time-zone.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-no-implicit-midnight.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-time-designator-required-for-disambiguation.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-time-separators.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-time-zone-annotation.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-unknown-annotation.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-with-time-designator.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-with-utc-designator.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-wrong-type.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-balance-negative-time-units.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-negative-epochnanoseconds.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/basic.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/custom.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/leap-second.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/limits.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/plaintime-propertybag-no-time-units.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/time-invalid.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/time-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/year-zero.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/basic.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-arguments.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-fields-iterable.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-fromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-invalid-return.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-monthdayfromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/duplicate-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/basic.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-arguments.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-fields-iterable.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-fromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-invalid-return.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-yearmonthfromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/duplicate-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/limits.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/basic.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendar-tostring.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-always.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-auto.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-critical.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-invalid-string.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-never.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-undefined.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-wrong-type.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/options-object.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/options-undefined.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/order-of-operations.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/year-format.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toStringTag/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toStringTag/prop-desc.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toStringTag/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-calendar-annotation.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-critical-unknown-annotation.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-date-with-utc-offset.js52
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-multiple-time-zone.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-no-implicit-midnight.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-time-designator-required-for-disambiguation.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-time-separators.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-time-zone-annotation.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-unknown-annotation.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-with-time-designator.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-with-utc-designator.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-wrong-type.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-zoneddatetime-negative-epochnanoseconds.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/basic.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/calendar.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/getpossibleinstantsfor-called-with-iso8601-calendar.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/order-of-operations.js98
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-balance-negative-time-units.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-propertybag-no-time-units.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-case-insensitive.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-not-callable.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getpossibleinstantsfor.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-datetime.js65
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-leap-second.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-multiple-offsets.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-year-zero.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-wrong-type.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/year-zero.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-leap-second.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-plaindatetime.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-case-insensitive.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-leap-second.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-string.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-calendar-annotation.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-critical-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-date-with-utc-offset.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-invalid.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-time-separators.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-time-zone-annotation.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-unknown-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/basic.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-dateadd-called-with-plaindate-instance.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-datefromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-fields-iterable.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-id-match.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-invalid-return.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-mismatch.js62
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/custom.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/days-in-month.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/days-in-year.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-default.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-higher-units.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-invalid-string.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-plurals-accepted.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-smallestunit-mismatch.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/order-of-operations.js214
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/round-cross-unit-boundary.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/rounding-relative.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/rounding-zero-year-month-week-length.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-nan.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-non-integer.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-out-of-range.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-undefined.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-ceil.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-expand.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-floor.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfCeil.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfEven.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfExpand.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfFloor.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfTrunc.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-invalid-string.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-trunc.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-undefined.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-higher-units.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-invalid-string.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-plurals-accepted.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/weeks-months.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/basic.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/basic.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/basic.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/builtin-calendar-no-observable-calls.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-fields-iterable.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-invalid-return.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-merge-fields-returns-primitive.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/copies-merge-fields-object.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/copy-properties-not-undefined.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/custom.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/duplicate-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/infinity-throws-rangeerror.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/options-undefined.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/order-of-operations.js80
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/overflow-invalid-string.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/overflow-undefined.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/overflow-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/plaindatelike-invalid.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/basic.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-case-insensitive.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-number.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-string-leap-second.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-string.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-temporal-object.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-wrong-type.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/missing-argument.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/subclassing-ignored.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/validate-calendar-value.js54
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/basic.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/validate-calendar-value.js55
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/subclass.js21
800 files changed, 22408 insertions, 0 deletions
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/argument-convert.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/argument-convert.js
new file mode 100644
index 0000000000..678c6a5f00
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/argument-convert.js
@@ -0,0 +1,55 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate
+description: PlainDate constructor with non-integer arguments.
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.assertPlainDate(new Temporal.PlainDate(2020.6, 11.7, 24.1),
+ 2020, 11, "M11", 24, "positive fractional");
+
+TemporalHelpers.assertPlainDate(new Temporal.PlainDate(-2020.6, 11.7, 24.1),
+ -2020, 11, "M11", 24, "negative fractional");
+
+TemporalHelpers.assertPlainDate(new Temporal.PlainDate(null, 11, 24),
+ 0, 11, "M11", 24, "null");
+
+TemporalHelpers.assertPlainDate(new Temporal.PlainDate(true, 11, 24),
+ 1, 11, "M11", 24, "boolean");
+
+TemporalHelpers.assertPlainDate(new Temporal.PlainDate("2020.6", "11.7", "24.1"),
+ 2020, 11, "M11", 24, "fractional strings");
+
+for (const invalid of [Symbol(), 1n]) {
+ assert.throws(TypeError, () => new Temporal.PlainDate(invalid, 11, 24), `year ${typeof invalid}`);
+ assert.throws(TypeError, () => new Temporal.PlainDate(2020, invalid, 24), `month ${typeof invalid}`);
+ assert.throws(TypeError, () => new Temporal.PlainDate(2020, 11, invalid), `day ${typeof invalid}`);
+}
+
+for (const invalid of [undefined, "invalid"]) {
+ assert.throws(RangeError, () => new Temporal.PlainDate(invalid, 11, 24), `year ${typeof invalid}`);
+ assert.throws(RangeError, () => new Temporal.PlainDate(2020, invalid, 24), `month ${typeof invalid}`);
+ assert.throws(RangeError, () => new Temporal.PlainDate(2020, 11, invalid), `day ${typeof invalid}`);
+}
+const actual = [];
+const args = [
+ TemporalHelpers.toPrimitiveObserver(actual, 2020, "year"),
+ TemporalHelpers.toPrimitiveObserver(actual, 11, "month"),
+ TemporalHelpers.toPrimitiveObserver(actual, 24, "day"),
+];
+TemporalHelpers.assertPlainDate(new Temporal.PlainDate(...args),
+ 2020, 11, "M11", 24, "invalid string");
+assert.compareArray(actual, [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+]);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/argument-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/argument-invalid.js
new file mode 100644
index 0000000000..4e1191ee98
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/argument-invalid.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate
+description: PlainDate constructor with invalid iso dates
+features: [Temporal]
+---*/
+
+const tests = [
+ [2020, 0, 24],
+ [2020, 13, 24],
+ [2020, -3, 24],
+ [2020, 12, 32],
+ [2020, 2, 30],
+ [2019, 2, 29],
+ [2019, 2, 0],
+ [2019, 2, -20],
+];
+
+for (const [year, month, day] of tests) {
+ assert.throws(RangeError, () => new Temporal.PlainDate(year, month, day),
+ `year=${year}, month=${month}, day=${day}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/basic.js
new file mode 100644
index 0000000000..fab877e5e5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/basic.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate
+description: Basic tests for the PlainDate constructor.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+Object.defineProperty(Temporal.Calendar, "from", {
+ get() {
+ throw new Test262Error("Should not get Calendar.from");
+ },
+});
+
+const calendar = new Temporal.Calendar("iso8601");
+const plainDateWithObject = new Temporal.PlainDate(2020, 12, 24, calendar);
+TemporalHelpers.assertPlainDate(plainDateWithObject, 2020, 12, "M12", 24, "with object");
+assert.sameValue(plainDateWithObject.getCalendar(), calendar);
+
+const plainDateWithString = new Temporal.PlainDate(2020, 12, 24, "iso8601");
+TemporalHelpers.assertPlainDate(plainDateWithString, 2020, 12, "M12", 24, "with string");
+assert.sameValue(plainDateWithString.getISOFields().calendar, "iso8601", "calendar slot should store a string");
+assert.notSameValue(plainDateWithString.getCalendar(), calendar);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/builtin.js
new file mode 100644
index 0000000000..bc862877d0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/builtin.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate
+description: Tests that Temporal.PlainDate meets the requirements for built-in objects
+info: |
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate),
+ Function.prototype, "prototype");
+
+assert.sameValue(typeof Temporal.PlainDate.prototype,
+ "object", "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-case-insensitive.js
new file mode 100644
index 0000000000..406dbc44c4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-case-insensitive.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate
+description: Calendar names are case-insensitive
+features: [Temporal]
+---*/
+
+const arg = "iSo8601";
+
+const result = new Temporal.PlainDate(2000, 5, 2, arg);
+assert.sameValue(result.calendarId, "iso8601", "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-number.js
new file mode 100644
index 0000000000..cd0f23b107
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-number.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate
+description: A number is not allowed to be a calendar
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ -19761118,
+ 19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => new Temporal.PlainDate(2000, 5, 2, arg),
+ "A number is not a valid ISO string for Calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-string.js
new file mode 100644
index 0000000000..d4be3beed6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-string.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.constructor
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const arg = "iso8601";
+
+const result = new Temporal.PlainDate(2000, 5, 2, arg);
+assert.sameValue(result.getISOFields().calendar, "iso8601", `Calendar created from string "${arg}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-temporal-object.js
new file mode 100644
index 0000000000..73f0b2f227
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-temporal-object.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal-totemporalcalendar step 1.b:
+ b. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2000, 5, 2);
+const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const plainMonthDay = new Temporal.PlainMonthDay(5, 2);
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 5);
+const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+[plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((arg) => {
+ const actual = [];
+ const expected = [];
+
+ const calendar = arg.getISOFields().calendar;
+
+ Object.defineProperty(arg, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ const result = new Temporal.PlainDate(2000, 5, 2, arg);
+ assert.sameValue(result.getISOFields().calendar, calendar, "Temporal object coerced to calendar");
+
+ assert.compareArray(actual, expected, "calendar getter not called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-undefined.js
new file mode 100644
index 0000000000..64d68fd512
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-undefined.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate
+description: Calendar argument defaults to the built-in ISO 8601 calendar
+features: [Temporal]
+---*/
+
+const args = [2020, 12, 24];
+
+Object.defineProperty(Temporal.Calendar, "from", {
+ get() {
+ throw new Test262Error("Should not get Calendar.from");
+ },
+});
+
+const dateExplicit = new Temporal.PlainDate(...args, undefined);
+assert.sameValue(dateExplicit.getISOFields().calendar, "iso8601", "calendar slot should store string");
+
+const dateImplicit = new Temporal.PlainDate(...args);
+assert.sameValue(dateImplicit.getISOFields().calendar, "iso8601", "calendar slot should store string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-wrong-type.js
new file mode 100644
index 0000000000..21897ea402
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-wrong-type.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for Calendar
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => new Temporal.PlainDate(2000, 5, 2, arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => new Temporal.PlainDate(2000, 5, 2, arg), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..33567d5266
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18));
+Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..08733a161f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-calendar-datefromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const arg1 = { year: 2000, month: 5, day: 2, calendar };
+const arg2 = new Temporal.PlainDate(1976, 11, 18);
+
+Temporal.PlainDate.compare(arg1, arg2);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar (first argument)");
+
+calendar.dateFromFieldsCallCount = 0;
+
+Temporal.PlainDate.compare(arg2, arg1);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar (first argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..d7f5b98479
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindate.prototype.compare
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+
+assert.throws(RangeError, () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..2898cce8e0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindate.prototype.compare
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+
+ assert.throws(RangeError, () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)));
+ assert.throws(RangeError, () => Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-number.js
new file mode 100644
index 0000000000..62c0596003
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-number.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)),
+ "A number is not a valid ISO string for PlainDate (first argument)"
+ );
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg),
+ "A number is not a valid ISO string for PlainDate (second argument)"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-object.js
new file mode 100644
index 0000000000..ad96dcbb27
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-object.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: basic object coercion in arguments
+features: [Temporal]
+---*/
+
+const d1 = Temporal.PlainDate.from("1976-11-18");
+const d2 = Temporal.PlainDate.from("2019-06-30");
+
+assert.sameValue(Temporal.PlainDate.compare({ year: 1976, month: 11, day: 18 }, d2), -1, "first argument");
+assert.sameValue(Temporal.PlainDate.compare({ year: 2019, month: 6, day: 30 }, d2), 0, "first argument");
+assert.sameValue(Temporal.PlainDate.compare({ year: 2024, month: 1, day: 12 }, d2), 1, "first argument");
+
+assert.sameValue(Temporal.PlainDate.compare(d1, { year: 2024, month: 1, day: 12 }), -1, "second argument");
+assert.sameValue(Temporal.PlainDate.compare(d1, { year: 1976, month: 11, day: 18 }), 0, "second argument");
+assert.sameValue(Temporal.PlainDate.compare(d1, { year: 1926, month: 7, day: 7 }), 1, "second argument");
+
+assert.throws(TypeError, () => Temporal.PlainDate.compare({ year: 1976 }, d2), "only year in first argument");
+assert.throws(TypeError, () => Temporal.PlainDate.compare(d1, { year: 2019 }), "only year in second argument");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-plaindatetime.js
new file mode 100644
index 0000000000..5666cdb980
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-plaindatetime.js
@@ -0,0 +1,53 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainDate by reading internal slots
+info: |
+ sec-temporal.plaindate.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalDate(_one_).
+ 2. Set _two_ to ? ToTemporalDate(_two_).
+ sec-temporal-totemporaldate step 2.b:
+ b. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ i. Return ! CreateTemporalDate(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const sameDate = new Temporal.PlainDate(2000, 5, 2);
+const earlierDate = new Temporal.PlainDate(1920, 7, 3);
+const laterDate = new Temporal.PlainDate(2005, 1, 12);
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const result = Temporal.PlainDate.compare(datetime, sameDate);
+ assert.sameValue(result, 0, "First argument, same date: comparison result");
+}, "First argument, same date");
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const result = Temporal.PlainDate.compare(datetime, earlierDate);
+ assert.sameValue(result, 1, "First argument, earlier date: comparison result");
+}, "First argument, earlier date");
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const result = Temporal.PlainDate.compare(datetime, laterDate);
+ assert.sameValue(result, -1, "First argument, later date: comparison result");
+}, "First argument, later date");
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const result = Temporal.PlainDate.compare(sameDate, datetime);
+ assert.sameValue(result, 0, "Second argument, same date: comparison result");
+}, "Second argument, same date");
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const result = Temporal.PlainDate.compare(earlierDate, datetime);
+ assert.sameValue(result, -1, "Second argument, earlier date: comparison result");
+}, "Second argument, earlier date");
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const result = Temporal.PlainDate.compare(laterDate, datetime);
+ assert.sameValue(result, 1, "Second argument, later date: comparison result");
+}, "Second argument, later date");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..531868629a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result1 = Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18));
+assert.sameValue(result1, 0, "Calendar is case-insensitive (first argument)");
+const result2 = Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg);
+assert.sameValue(result2, 0, "Calendar is case-insensitive (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..34e69918a2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result1 = Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18));
+assert.sameValue(result1, 0, "leap second is a valid ISO string for calendar (first argument)");
+const result2 = Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg);
+assert.sameValue(result2, 0, "leap second is a valid ISO string for calendar (first argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..076c5088cf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-number.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)),
+ "A number is not a valid ISO string for calendar (first argument)"
+ );
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg),
+ "A number is not a valid ISO string for calendar (second argument)"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..3c47b5e003
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-string.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateFromFields should not be looked up");
+ },
+});
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+
+const result1 = Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18));
+assert.sameValue(result1, 0, `Calendar created from string "${arg}" (first argument)`);
+
+const result2 = Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg);
+assert.sameValue(result2, 0, `Calendar created from string "${arg}" (second argument)`);
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", dateFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..a347aeee90
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,49 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === "string" ? RangeError : TypeError,
+ () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)),
+ `${description} does not convert to a valid ISO string (first argument)`
+ );
+ assert.throws(
+ typeof calendar === "string" ? RangeError : TypeError,
+ () => Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg),
+ `${description} does not convert to a valid ISO string (second argument)`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)), `${description} is not a valid property bag and does not convert to a string (first argument)`);
+ assert.throws(TypeError, () => Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg), `${description} is not a valid property bag and does not convert to a string (second argument)`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..4bee8a16e2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-year-zero.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)),
+ "reject minus zero as extended year (first argument)"
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg),
+ "reject minus zero as extended year (second argument)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..70463984e9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindate.prototype.compare
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+
+assert.throws(RangeError, () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..67725aae57
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-calendar-annotation.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainDate.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..375258ee4b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!foo=bar]",
+ "1970-01-01T00:00[!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)),
+ `reject unknown annotation with critical flag: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg),
+ `reject unknown annotation with critical flag: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..a2d429d48f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-date-with-utc-offset.js
@@ -0,0 +1,51 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = Temporal.PlainDate.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+const invalidStrings = [
+ "2022-09-15Z",
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)),
+ `"${arg}" UTC offset without time is not valid for PlainDate (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg),
+ `"${arg}" UTC offset without time is not valid for PlainDate (second argument)`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-invalid.js
new file mode 100644
index 0000000000..7e50608f49
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-invalid.js
@@ -0,0 +1,69 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const other = new Temporal.PlainDate(2020, 1, 1);
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.compare(arg, other),
+ `"${arg}" should not be a valid ISO string for a PlainDate (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.compare(other, arg),
+ `"${arg}" should not be a valid ISO string for a PlainDate (second argument)`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..816af9c8cb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-multiple-calendar.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)),
+ `reject more than one calendar annotation if any critical: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg),
+ `reject more than one calendar annotation if any critical: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..ea7794faf2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-multiple-time-zone.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[UTC][UTC]",
+ "1970-01-01T00:00[UTC][UTC]",
+ "1970-01-01T00:00[!UTC][UTC]",
+ "1970-01-01T00:00[UTC][!UTC]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00[UTC][foo=bar][UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)),
+ `reject more than one time zone annotation: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg),
+ `reject more than one time zone annotation: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-time-separators.js
new file mode 100644
index 0000000000..7d3a055703
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-time-separators.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+tests.forEach(([arg, description]) => {
+ assert.sameValue(
+ Temporal.PlainDate.compare(arg, date),
+ 0,
+ `variant time separators (${description}), first argument`
+ );
+
+ assert.sameValue(
+ Temporal.PlainDate.compare(date, arg),
+ 0,
+ `variant time separators (${description}), second argument`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..a7cd502511
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-time-zone-annotation.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ['2000-05-02[Asia/Kolkata]', 'named, with no time'],
+ ['2000-05-02[!Europe/Vienna]', 'named, with ! and no time'],
+ ['2000-05-02[+00:00]', 'numeric, with no time'],
+ ['2000-05-02[!-02:30]', 'numeric, with ! and no time'],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainDate.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..869ac1e464
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-unknown-annotation.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainDate.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..d434dbc6a6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-with-utc-designator.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const plainDate = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.compare(arg, plainDate),
+ "String with UTC designator should not be valid as a PlainDate (first argument)"
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.compare(plainDate, arg),
+ "String with UTC designator should not be valid as a PlainDate (second argument)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string.js
new file mode 100644
index 0000000000..cb7969a861
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: basic string coercion in arguments
+features: [Temporal]
+---*/
+
+const d1 = Temporal.PlainDate.from("1976-11-18");
+const d2 = Temporal.PlainDate.from("2019-06-30");
+
+assert.sameValue(Temporal.PlainDate.compare("1976-11-18", d2), -1, "first argument");
+assert.sameValue(Temporal.PlainDate.compare("2019-06-30", d2), 0, "first argument");
+assert.sameValue(Temporal.PlainDate.compare("2024-01-12", d2), 1, "first argument");
+
+assert.sameValue(Temporal.PlainDate.compare(d1, "2019-06-30"), -1, "second argument");
+assert.sameValue(Temporal.PlainDate.compare(d1, "1976-11-18"), 0, "second argument");
+assert.sameValue(Temporal.PlainDate.compare(d1, "1926-07-07"), 1, "second argument");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-wrong-type.js
new file mode 100644
index 0000000000..29d8f2ba6a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-wrong-type.js
@@ -0,0 +1,47 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)),
+ `${description} does not convert to a valid ISO string (first argument)`
+ );
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg),
+ `${description} does not convert to a valid ISO string (second argument)`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)), `${description} is not a valid property bag and does not convert to a string (first argument)`);
+ assert.throws(TypeError, () => Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg), `${description} is not a valid property bag and does not convert to a string (second argument)`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..deb5803a86
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-slots.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18));
+Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg);
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..7dcdaf5d6f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, Infinity, -Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+
+ assert.throws(RangeError, () => Temporal.PlainDate.compare(datetime, date));
+ assert.throws(RangeError, () => Temporal.PlainDate.compare(date, datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..2ecaa0ad9d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDate.compare(datetime, date),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDate.compare(date, datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..45bb1081c6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+
+ assert.throws(RangeError, () => Temporal.PlainDate.compare(datetime, date));
+ assert.throws(RangeError, () => Temporal.PlainDate.compare(date, datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..669a216d5b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+
+ assert.throws(TypeError, () => Temporal.PlainDate.compare(datetime, date));
+ assert.throws(TypeError, () => Temporal.PlainDate.compare(date, datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime.js
new file mode 100644
index 0000000000..a6654b357b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: ZonedDateTime is supported.
+features: [Temporal]
+---*/
+
+const zdt = new Temporal.ZonedDateTime(0n, "UTC");
+assert.sameValue(
+ Temporal.PlainDate.compare(zdt, new Temporal.PlainDate(1970, 1, 1)),
+ 0, "same date, ZDT first");
+assert.sameValue(
+ Temporal.PlainDate.compare(new Temporal.PlainDate(1970, 1, 1), zdt),
+ 0, "same date, ZDT second");
+assert.sameValue(
+ Temporal.PlainDate.compare(zdt, new Temporal.PlainDate(1976, 11, 18)),
+ -1, "different date, ZDT first");
+assert.sameValue(
+ Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), zdt),
+ 1, "different date, ZDT second");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/basic.js
new file mode 100644
index 0000000000..ffb86010e0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/basic.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: basic tests
+features: [Temporal]
+---*/
+
+const d1 = Temporal.PlainDate.from("1976-11-18");
+const d2 = Temporal.PlainDate.from("2019-06-30");
+const d3 = Temporal.PlainDate.from("2019-06-30");
+assert.sameValue(Temporal.PlainDate.compare(d1, d1), 0, "same object");
+assert.sameValue(Temporal.PlainDate.compare(d1, d2), -1, "earlier");
+assert.sameValue(Temporal.PlainDate.compare(d2, d1), 1, "later");
+assert.sameValue(Temporal.PlainDate.compare(d2, d3), 0, "same date");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/builtin.js
new file mode 100644
index 0000000000..943c3e4c9b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/builtin.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: Tests that Temporal.PlainDate.compare meets the requirements for built-in objects
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.compare),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.compare),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.compare),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.compare.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..4884cd37a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar-datefromfields-called-with-options-undefined.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.compare
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+Temporal.PlainDate.compare({ year: 2000, month: 5, day: 2, calendar }, { year: 2000, month: 5, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar-fields-iterable.js
new file mode 100644
index 0000000000..08ec8dd712
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar-fields-iterable.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindate.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalDate(_one_).
+ 2. Set _two_ to ? ToTemporalDate(_two_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+Temporal.PlainDate.compare(
+ { year: 2000, month: 5, day: 2, calendar: calendar1 },
+ { year: 2001, month: 6, day: 3, calendar: calendar2 },
+);
+
+assert.sameValue(calendar1.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar1.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar1.iteratorExhausted[0], "iterated through the whole iterable");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar-temporal-object.js
new file mode 100644
index 0000000000..e7f8b6216a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar-temporal-object.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindate.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalDate(_one_).
+ 2. Set _two_ to ? ToTemporalDate(_two_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ Temporal.PlainDate.compare(
+ { year: 2000, month: 5, day: 2, calendar: temporalObject },
+ { year: 2001, month: 6, day: 3, calendar: temporalObject },
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar.js
new file mode 100644
index 0000000000..6089850753
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: basic tests
+features: [Temporal]
+---*/
+
+class CalendarTraceToString extends Temporal.Calendar {
+ constructor(id) {
+ super("iso8601");
+ this.id_ = id;
+ this.calls = 0;
+ }
+ toString() {
+ ++this.calls;
+ return this.id_;
+ }
+};
+
+const calendar1 = new CalendarTraceToString("a");
+const date1 = new Temporal.PlainDate(1914, 2, 23, calendar1);
+
+const calendar2 = new CalendarTraceToString("a");
+const date2 = new Temporal.PlainDate(1914, 2, 23, calendar2);
+
+const calendar3 = new CalendarTraceToString("b");
+const date3 = new Temporal.PlainDate(1914, 2, 23, calendar3);
+
+assert.sameValue(Temporal.PlainDate.compare(date1, date1), 0, "same object");
+assert.sameValue(Temporal.PlainDate.compare(date1, date2), 0, "same date");
+assert.sameValue(Temporal.PlainDate.compare(date1, date3), 0, "same date, different calendar");
+
+assert.sameValue(calendar1.calls, 0, "calendar1 toString() calls");
+assert.sameValue(calendar2.calls, 0, "calendar2 toString() calls");
+assert.sameValue(calendar3.calls, 0, "calendar3 toString() calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/exhaustive.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/exhaustive.js
new file mode 100644
index 0000000000..9858787bcb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/exhaustive.js
@@ -0,0 +1,71 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: Tests for compare() with each possible outcome
+features: [Temporal]
+---*/
+
+const cal1 = "iso8601";
+const cal2 = new (class extends Temporal.Calendar { id = "custom"; })("iso8601");
+
+assert.sameValue(
+ Temporal.PlainDate.compare(
+ new Temporal.PlainDate(2000, 5, 31, cal1),
+ new Temporal.PlainDate(1987, 5, 31, cal2)
+ ),
+ 1,
+ "year >"
+);
+assert.sameValue(
+ Temporal.PlainDate.compare(
+ new Temporal.PlainDate(1981, 12, 15, cal1),
+ new Temporal.PlainDate(2048, 12, 15, cal2)
+ ),
+ -1,
+ "year <"
+);
+assert.sameValue(
+ Temporal.PlainDate.compare(
+ new Temporal.PlainDate(2000, 5, 31, cal1),
+ new Temporal.PlainDate(2000, 3, 31, cal2)
+ ),
+ 1,
+ "month >"
+);
+assert.sameValue(
+ Temporal.PlainDate.compare(
+ new Temporal.PlainDate(1981, 4, 15, cal1),
+ new Temporal.PlainDate(1981, 12, 15, cal2)
+ ),
+ -1,
+ "month <"
+);
+assert.sameValue(
+ Temporal.PlainDate.compare(
+ new Temporal.PlainDate(2000, 5, 31, cal1),
+ new Temporal.PlainDate(2000, 5, 14, cal2)
+ ),
+ 1,
+ "day >"
+);
+assert.sameValue(
+ Temporal.PlainDate.compare(
+ new Temporal.PlainDate(1981, 4, 15, cal1),
+ new Temporal.PlainDate(1981, 4, 21, cal2)
+ ),
+ -1,
+ "day <"
+);
+assert.sameValue(
+ Temporal.PlainDate.compare(
+ new Temporal.PlainDate(2000, 5, 31, cal1),
+ new Temporal.PlainDate(2000, 5, 31, cal2)
+ ),
+ 0,
+ "="
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..0f62ef3be2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/infinity-throws-rangeerror.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in a property bag for either argument is Infinity or -Infinity
+esid: sec-temporal.plaindate.compare
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const other = new Temporal.PlainDate(2000, 5, 2);
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => Temporal.PlainDate.compare({ ...base, [prop]: inf }, other), `${prop} property cannot be ${inf}`);
+
+ assert.throws(RangeError, () => Temporal.PlainDate.compare(other, { ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls1 = [];
+ const obj1 = TemporalHelpers.toPrimitiveObserver(calls1, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainDate.compare({ ...base, [prop]: obj1 }, other));
+ assert.compareArray(calls1, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+
+ const calls2 = [];
+ const obj2 = TemporalHelpers.toPrimitiveObserver(calls2, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainDate.compare(other, { ...base, [prop]: obj2 }));
+ assert.compareArray(calls2, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/leap-second.js
new file mode 100644
index 0000000000..a815452419
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/leap-second.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: Leap second is a valid ISO string for PlainDate
+features: [Temporal]
+---*/
+
+let arg = "2016-12-31T23:59:60";
+let result = Temporal.PlainDate.compare(arg, new Temporal.PlainDate(2016, 12, 31));
+assert.sameValue(result, 0, "leap second is a valid ISO string for PlainDate (first argument)");
+result = Temporal.PlainDate.compare(new Temporal.PlainDate(2016, 12, 31), arg);
+assert.sameValue(result, 0, "leap second is a valid ISO string for PlainDate (second argument)");
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+result = Temporal.PlainDate.compare(arg, new Temporal.PlainDate(2016, 12, 31));
+assert.sameValue(result, 0, "second: 60 is ignored in property bag for PlainDate (first argument)");
+result = Temporal.PlainDate.compare(new Temporal.PlainDate(2016, 12, 31), arg);
+assert.sameValue(result, 0, "second: 60 is ignored in property bag for PlainDate (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/length.js
new file mode 100644
index 0000000000..97245bc5b9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: Temporal.PlainDate.compare.length is 2
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.compare, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/name.js
new file mode 100644
index 0000000000..ea93f5cc06
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: Temporal.PlainDate.compare.name is "compare"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.compare, "name", {
+ value: "compare",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/not-a-constructor.js
new file mode 100644
index 0000000000..d7ee33d50d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/not-a-constructor.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: Temporal.PlainDate.compare does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.compare();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.compare), false,
+ "isConstructor(Temporal.PlainDate.compare)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/prop-desc.js
new file mode 100644
index 0000000000..39f85f1760
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: The "compare" property of Temporal.PlainDate
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.compare,
+ "function",
+ "`typeof PlainDate.compare` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate, "compare", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/use-internal-slots.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/use-internal-slots.js
new file mode 100644
index 0000000000..2f8a0c4a2a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/use-internal-slots.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-compareisodate
+description: compare() ignores the observable properties and uses internal slots
+features: [Temporal]
+---*/
+
+function CustomError() {}
+
+class AvoidGettersDate extends Temporal.PlainDate {
+ get year() {
+ throw new CustomError();
+ }
+ get month() {
+ throw new CustomError();
+ }
+ get day() {
+ throw new CustomError();
+ }
+}
+
+const one = new AvoidGettersDate(2000, 5, 2);
+const two = new AvoidGettersDate(2006, 3, 25);
+assert.sameValue(Temporal.PlainDate.compare(one, two), -1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/year-zero.js
new file mode 100644
index 0000000000..4af85b15ae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/year-zero.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Negative zero, as an extended year, fails
+esid: sec-temporal.plaindate.compare
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(RangeError,
+ () => Temporal.PlainDate.compare(arg, instance),
+ "Minus zero is an invalid extended year (first argument)"
+ );
+
+ assert.throws(RangeError,
+ () => Temporal.PlainDate.compare(instance, arg),
+ "Minus zero is an invalid extended year (second argument)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/constructor.js
new file mode 100644
index 0000000000..091305f0e3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/constructor.js
@@ -0,0 +1,15 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate
+description: Temporal.PlainDate constructor cannot be called as a function
+info: |
+ 1. If NewTarget is undefined, throw a TypeError exception.
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => Temporal.PlainDate(1970, 1, 2));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..4bc627ff84
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+Temporal.PlainDate.from(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..0a72943f56
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-calendar-datefromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const arg = { year: 2000, month: 5, day: 2, calendar };
+Temporal.PlainDate.from(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..a3e0d96bf8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindate.prototype.from
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+
+assert.throws(RangeError, () => Temporal.PlainDate.from(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..bf3482afba
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-duplicate-calendar-fields.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindate.prototype.from
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+
+ assert.throws(RangeError, () => Temporal.PlainDate.from(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-leap-second.js
new file mode 100644
index 0000000000..c3436f7d6b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-leap-second.js
@@ -0,0 +1,44 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Leap second is a valid ISO string for PlainDate
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let arg = "2016-12-31T23:59:60";
+
+const result1 = Temporal.PlainDate.from(arg);
+TemporalHelpers.assertPlainDate(
+ result1,
+ 2016, 12, "M12", 31,
+ "leap second is a valid ISO string for PlainDate"
+);
+
+const result2 = Temporal.PlainDate.from(arg, { overflow: "reject" });
+TemporalHelpers.assertPlainDate(
+ result2,
+ 2016, 12, "M12", 31,
+ "leap second is a valid ISO string for PlainDate"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+
+const result3 = Temporal.PlainDate.from(arg);
+TemporalHelpers.assertPlainDate(
+ result3,
+ 2016, 12, "M12", 31,
+ "second: 60 is ignored in property bag for PlainDate"
+);
+
+const result4 = Temporal.PlainDate.from(arg, { overflow: "reject" });
+TemporalHelpers.assertPlainDate(
+ result4,
+ 2016, 12, "M12", 31,
+ "second: 60 is ignored in property bag for PlainDate even with overflow: reject"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-number.js
new file mode 100644
index 0000000000..492564b840
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-number.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDate.from(arg),
+ 'Numbers cannot be used in place of an ISO string for PlainDate'
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-object-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-object-invalid.js
new file mode 100644
index 0000000000..5c683bcff3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-object-invalid.js
@@ -0,0 +1,44 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Property bag is correctly converted into PlainDate
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const badFields = { year: 2019, month: 1, day: 32 };
+assert.throws(RangeError, () => Temporal.PlainDate.from(badFields, { overflow: "reject" }),
+ "bad fields with reject");
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from(badFields),
+ 2019, 1, "M01", 31, "bad fields with missing overflow");
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from(badFields, { overflow: "constrain" }),
+ 2019, 1, "M01", 31, "bad fields with constrain");
+
+assert.throws(RangeError,
+ () => Temporal.PlainDate.from({ year: 1976, month: 11, monthCode: "M12", day: 18 }),
+ "month and monthCode must agree");
+
+assert.throws(TypeError,
+ () => Temporal.PlainDate.from({ year: 2019, day: 15 }),
+ "missing month");
+
+assert.throws(TypeError,
+ () => Temporal.PlainDate.from({}),
+ "no properties");
+
+assert.throws(TypeError,
+ () => Temporal.PlainDate.from({ month: 12 }),
+ "missing year, day");
+
+assert.throws(TypeError,
+ () => Temporal.PlainDate.from({ year: 1976, months: 11, day: 18 }),
+ "misspelled month");
+
+assert.throws(TypeError,
+ () => Temporal.PlainDate.from({ year: undefined, month: 11, day: 18 }),
+ "year undefined");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-object-valid.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-object-valid.js
new file mode 100644
index 0000000000..c02ee292b4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-object-valid.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Property bag is correctly converted into PlainDate
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const valid = [
+ [
+ { year: 2019, month: 10, monthCode: "M10", day: 1, hour: 14, minute: 20, second: 36 },
+ 2019, 10, "M10", 1
+ ],
+ [
+ { year: 1976, month: 11, day: 18 },
+ 1976, 11, "M11", 18
+ ],
+ [
+ { year: 1976, monthCode: "M11", day: 18 },
+ 1976, 11, "M11", 18
+ ],
+ [
+ { year: 1976, month: 11, day: 18, days: 15 },
+ 1976, 11, "M11", 18
+ ],
+];
+
+for (const [dateTimeFields, ...expected] of valid) {
+ const plainDate = Temporal.PlainDate.from(dateTimeFields);
+ TemporalHelpers.assertPlainDate(plainDate, ...expected, `from(${JSON.stringify(dateTimeFields)}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-plaindate.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-plaindate.js
new file mode 100644
index 0000000000..38c381790e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-plaindate.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: A PlainDate object is copied, not returned directly
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const orig = new Temporal.PlainDate(2000, 5, 2);
+const result = Temporal.PlainDate.from(orig);
+
+TemporalHelpers.assertPlainDate(
+ result,
+ 2000, 5, "M05", 2,
+ "PlainDate is copied"
+);
+
+assert.sameValue(result.getISOFields().calendar, orig.getISOFields().calendar, "Calendar is copied");
+
+assert.notSameValue(
+ result,
+ orig,
+ "When a PlainDate is given, the returned value is not the original PlainDate"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-plaindatetime.js
new file mode 100644
index 0000000000..a98ce76168
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-plaindatetime.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainDate by reading internal slots
+info: |
+ sec-temporal.plaindate.from step 3:
+ 3. Return ? ToTemporalDate(_item_, _options_).
+ sec-temporal-totemporaldate step 2.b:
+ b. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ i. Return ! CreateTemporalDate(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime, calendar) => {
+ const result = Temporal.PlainDate.from(datetime);
+ TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 2);
+ assert.sameValue(result.getCalendar(), calendar, "calendar result");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..877c77b77b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: The calendar name is case-insensitive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = Temporal.PlainDate.from(arg);
+TemporalHelpers.assertPlainDate(result, 1976, 11, "M11", 18, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..21fea60629
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Leap second is a valid ISO string for a calendar in a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = Temporal.PlainDate.from(arg);
+TemporalHelpers.assertPlainDate(
+ result,
+ 1976, 11, "M11", 18,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..f33a84fb5e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-number.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDate.from(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..405ef9d022
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-string.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = Temporal.PlainDate.from(arg);
+TemporalHelpers.assertPlainDate(result, 1976, 11, "M11", 18, `Calendar created from string "${calendar}"`);
+assert.sameValue(result.getISOFields().calendar, "iso8601", "calendar slot stores a string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..0fc09911a1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => Temporal.PlainDate.from(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => Temporal.PlainDate.from(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..fc65d8fd12
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-year-zero.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.from(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar.js
new file mode 100644
index 0000000000..8c9ae4c2a9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Property bag is correctly converted into PlainDate
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+const plainDate = Temporal.PlainDate.from({ year: 1976, month: 11, day: 18, calendar });
+TemporalHelpers.assertPlainDate(plainDate, 1976, 11, "M11", 18);
+assert.sameValue(plainDate.getISOFields().calendar, "iso8601", "calendar slot should store a string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..19253021b2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindate.prototype.from
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+
+assert.throws(RangeError, () => Temporal.PlainDate.from(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..d7d6696d2f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-calendar-annotation.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainDate.from(arg);
+
+ TemporalHelpers.assertPlainDate(
+ result,
+ 2000, 5, "M05", 2,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..b1ac214b95
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!foo=bar]",
+ "1970-01-01T00:00[!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.from(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..14cba00fe5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-date-with-utc-offset.js
@@ -0,0 +1,47 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = Temporal.PlainDate.from(arg);
+
+ TemporalHelpers.assertPlainDate(
+ result,
+ 2000, 5, "M05", 2,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+const invalidStrings = [
+ "2022-09-15Z",
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.from(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-invalid.js
new file mode 100644
index 0000000000..d8cd182a20
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-invalid.js
@@ -0,0 +1,63 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.from(arg),
+ `"${arg}" should not be a valid ISO string for a PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..a305783053
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-multiple-calendar.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.from(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..13fd43cbcb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-multiple-time-zone.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[UTC][UTC]",
+ "1970-01-01T00:00[UTC][UTC]",
+ "1970-01-01T00:00[!UTC][UTC]",
+ "1970-01-01T00:00[UTC][!UTC]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00[UTC][foo=bar][UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.from(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-time-separators.js
new file mode 100644
index 0000000000..6333981784
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-time-separators.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainDate.from(arg);
+
+ TemporalHelpers.assertPlainDate(
+ result,
+ 2000, 5, "M05", 2,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..6593865f70
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-time-zone-annotation.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[Asia/Kolkata]", "named, with no time"],
+ ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"],
+ ["2000-05-02[+00:00]", "numeric, with no time"],
+ ["2000-05-02[!-02:30]", "numeric, with ! and no time"],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainDate.from(arg);
+
+ TemporalHelpers.assertPlainDate(
+ result,
+ 2000, 5, "M05", 2,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-trailing-junk.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-trailing-junk.js
new file mode 100644
index 0000000000..d14560a938
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-trailing-junk.js
@@ -0,0 +1,14 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: RangeError thrown if a string with trailing junk is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+assert.throws(RangeError, () => Temporal.PlainDate.from("1976-11-18junk"),
+ "String with trailing junk should not be valid as a PlainDate");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..ff0eb36df6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-unknown-annotation.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainDate.from(arg);
+
+ TemporalHelpers.assertPlainDate(
+ result,
+ 2000, 5, "M05", 2,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..bdcf201bc4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-with-utc-designator.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.from(arg),
+ "String with UTC designator should not be valid as a PlainDate"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string.js
new file mode 100644
index 0000000000..c56d47ca2c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string.js
@@ -0,0 +1,42 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: various interesting string arguments.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1976-11-18", 1976, 11, "M11", 18],
+ ["2019-06-30", 2019, 6, "M06", 30],
+ ["+000050-06-30", 50, 6, "M06", 30],
+ ["+010583-06-30", 10583, 6, "M06", 30],
+ ["-010583-06-30", -10583, 6, "M06", 30],
+ ["-000333-06-30", -333, 6, "M06", 30],
+ ["19761118", 1976, 11, "M11", 18],
+ ["+0019761118", 1976, 11, "M11", 18],
+ ["1976-11-18T152330.1+00:00", 1976, 11, "M11", 18],
+ ["19761118T15:23:30.1+00:00", 1976, 11, "M11", 18],
+ ["1976-11-18T15:23:30.1+0000", 1976, 11, "M11", 18],
+ ["1976-11-18T152330.1+0000", 1976, 11, "M11", 18],
+ ["19761118T15:23:30.1+0000", 1976, 11, "M11", 18],
+ ["19761118T152330.1+00:00", 1976, 11, "M11", 18],
+ ["19761118T152330.1+0000", 1976, 11, "M11", 18],
+ ["+001976-11-18T152330.1+00:00", 1976, 11, "M11", 18],
+ ["+0019761118T15:23:30.1+00:00", 1976, 11, "M11", 18],
+ ["+001976-11-18T15:23:30.1+0000", 1976, 11, "M11", 18],
+ ["+001976-11-18T152330.1+0000", 1976, 11, "M11", 18],
+ ["+0019761118T15:23:30.1+0000", 1976, 11, "M11", 18],
+ ["+0019761118T152330.1+00:00", 1976, 11, "M11", 18],
+ ["+0019761118T152330.1+0000", 1976, 11, "M11", 18],
+];
+
+for (const [input, ...expected] of tests) {
+ const result = Temporal.PlainDate.from(input);
+ TemporalHelpers.assertPlainDate(result, ...expected, `from(${input})`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-wrong-type.js
new file mode 100644
index 0000000000..8f7cf439c2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-wrong-type.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => Temporal.PlainDate.from(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.PlainDate.from(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-convert.js
new file mode 100644
index 0000000000..0134e8ab9a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+assert.throws(Test262Error, () => Temporal.PlainDate.from(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..31e2dea78c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-slots.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+Temporal.PlainDate.from(arg);
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..7b4c3b2721
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => Temporal.PlainDate.from(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..f8c5a7164c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindate.from
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach(notCallable => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDate.from(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..775095b425
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => Temporal.PlainDate.from(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..cca5f38eb7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => Temporal.PlainDate.from(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime.js
new file mode 100644
index 0000000000..1601aa288a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: A ZonedDateTime object is handled separately
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+const zdt = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar);
+const result = Temporal.PlainDate.from(zdt);
+
+TemporalHelpers.assertPlainDate(
+ result,
+ 2001, 9, "M09", 9,
+ "ZonedDateTime is converted"
+);
+
+assert.sameValue(
+ result.getCalendar(),
+ calendar,
+ "Calendar is copied"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/builtin.js
new file mode 100644
index 0000000000..2658a4878b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/builtin.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Tests that Temporal.PlainDate.from meets the requirements for built-in objects
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.from),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.from),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.from),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.from.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/calendar-fields-custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/calendar-fields-custom.js
new file mode 100644
index 0000000000..8c14dc3d6b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/calendar-fields-custom.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Verify the result of calendar.fields() is treated correctly.
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateFromFields(fields) {
+ assert.compareArray(Object.keys(fields), ["a", "b"]);
+ return new Temporal.PlainDate(2020, 7, 4);
+ }
+ fields(fields) {
+ assert.compareArray(fields, ["day", "month", "monthCode", "year"]);
+ return ["b", "a"];
+ }
+}
+
+const calendar = new CustomCalendar();
+const actual = [];
+const item = TemporalHelpers.propertyBagObserver(actual, { calendar }, "item");
+
+const plainDate = Temporal.PlainDate.from(item);
+TemporalHelpers.assertPlainDate(plainDate, 2020, 7, "M07", 4);
+assert.compareArray(actual, [
+ "get item.calendar",
+ "get item.a",
+ "get item.b",
+]);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/calendar-fields-iterable.js
new file mode 100644
index 0000000000..3fc52b4530
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/calendar-fields-iterable.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindate.from step 3:
+ 3. Return ? ToTemporalDate(_item_, _options_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+Temporal.PlainDate.from({ year: 2000, month: 5, day: 2, calendar });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/calendar-temporal-object.js
new file mode 100644
index 0000000000..9d488d4eb1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindate.from step 3:
+ 3. Return ? ToTemporalDate(_item_, _options_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => {
+ const result = Temporal.PlainDate.from({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+ assert.sameValue(result.getCalendar(), calendar, "Temporal object coerced to calendar");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..67480076c9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/infinity-throws-rangeerror.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindate.from
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => Temporal.PlainDate.from({ ...base, [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainDate.from({ ...base, [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/length.js
new file mode 100644
index 0000000000..f6c885ad4d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Temporal.PlainDate.from.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.from, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/limits.js
new file mode 100644
index 0000000000..0d223c582f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/limits.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: PlainDate.from enforces the supported range
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const tooEarly = { year: -271821, month: 4, day: 18 };
+const tooLate = { year: 275760, month: 9, day: 14 };
+["reject", "constrain"].forEach((overflow) => {
+ [tooEarly, tooLate, "-271821-04-18", "+275760-09-14"].forEach((value) => {
+ assert.throws(RangeError, () => Temporal.PlainDate.from(value, { overflow }),
+ `${JSON.stringify(value)} with ${overflow}`);
+ });
+});
+
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from({ year: -271821, month: 4, day: 19 }),
+ -271821, 4, "M04", 19, "min object");
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from({ year: 275760, month: 9, day: 13 }),
+ 275760, 9, "M09", 13, "max object");
+
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from("-271821-04-19"),
+ -271821, 4, "M04", 19, "min string");
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from("+275760-09-13"),
+ 275760, 9, "M09", 13, "max string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/name.js
new file mode 100644
index 0000000000..c1e782a12a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Temporal.PlainDate.from.name is "from"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.from, "name", {
+ value: "from",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/not-a-constructor.js
new file mode 100644
index 0000000000..019313865d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/not-a-constructor.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Temporal.PlainDate.from does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.from();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.from), false,
+ "isConstructor(Temporal.PlainDate.from)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/observable-get-overflow-argument-primitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/observable-get-overflow-argument-primitive.js
new file mode 100644
index 0000000000..529b32d6d3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/observable-get-overflow-argument-primitive.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: overflow property is extracted with string argument.
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+
+let actual = [];
+const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options");
+
+const result = Temporal.PlainDate.from("2021-05-17", options);
+assert.compareArray(actual, expected, "Successful call");
+TemporalHelpers.assertPlainDate(result, 2021, 5, "M05", 17);
+
+actual.splice(0); // empty it for the next check
+const failureExpected = [
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+];
+
+assert.throws(TypeError, () => Temporal.PlainDate.from(7, options));
+assert.compareArray(actual, failureExpected, "Failing call");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/observable-get-overflow-argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/observable-get-overflow-argument-string-invalid.js
new file mode 100644
index 0000000000..7d9308e508
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/observable-get-overflow-argument-string-invalid.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: overflow property is extracted with ISO-invalid string argument.
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+];
+
+let actual = [];
+const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options");
+
+assert.throws(RangeError, () => Temporal.PlainDate.from("2020-13-34", options));
+assert.compareArray(actual, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-object.js
new file mode 100644
index 0000000000..4a4a89f966
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-object.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.from
+description: Empty object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.assertPlainDate(
+ Temporal.PlainDate.from({ year: 1976, month: 11, day: 18 }, {}), 1976, 11, "M11", 18,
+ "options may be an empty plain object"
+);
+
+TemporalHelpers.assertPlainDate(
+ Temporal.PlainDate.from({ year: 1976, month: 11, day: 18 }, () => {}), 1976, 11, "M11", 18,
+ "options may be an empty function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-undefined.js
new file mode 100644
index 0000000000..15b317f3ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-undefined.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const fields = { year: 2000, month: 13, day: 2 };
+
+const explicit = Temporal.PlainDate.from(fields, undefined);
+assert.sameValue(explicit.month, 12, "default overflow is constrain");
+
+const implicit = Temporal.PlainDate.from(fields);
+assert.sameValue(implicit.month, 12, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-wrong-type.js
new file mode 100644
index 0000000000..5c5eb1fd63
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-wrong-type.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+for (const value of badOptions) {
+ assert.throws(TypeError, () => Temporal.PlainDate.from({ year: 1976, month: 11, day: 18 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/order-of-operations.js
new file mode 100644
index 0000000000..513ddcf51f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/order-of-operations.js
@@ -0,0 +1,79 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Properties on an object passed to from() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ "getOwnPropertyDescriptor options.extra",
+ "get options.extra",
+ "get fields.calendar",
+ "has fields.calendar.dateAdd",
+ "has fields.calendar.dateFromFields",
+ "has fields.calendar.dateUntil",
+ "has fields.calendar.day",
+ "has fields.calendar.dayOfWeek",
+ "has fields.calendar.dayOfYear",
+ "has fields.calendar.daysInMonth",
+ "has fields.calendar.daysInWeek",
+ "has fields.calendar.daysInYear",
+ "has fields.calendar.fields",
+ "has fields.calendar.id",
+ "has fields.calendar.inLeapYear",
+ "has fields.calendar.mergeFields",
+ "has fields.calendar.month",
+ "has fields.calendar.monthCode",
+ "has fields.calendar.monthDayFromFields",
+ "has fields.calendar.monthsInYear",
+ "has fields.calendar.weekOfYear",
+ "has fields.calendar.year",
+ "has fields.calendar.yearMonthFromFields",
+ "has fields.calendar.yearOfWeek",
+ "get fields.calendar.dateFromFields",
+ "get fields.calendar.fields",
+ "call fields.calendar.fields",
+ "get fields.day",
+ "get fields.day.valueOf",
+ "call fields.day.valueOf",
+ "get fields.month",
+ "get fields.month.valueOf",
+ "call fields.month.valueOf",
+ "get fields.monthCode",
+ "get fields.monthCode.toString",
+ "call fields.monthCode.toString",
+ "get fields.year",
+ "get fields.year.valueOf",
+ "call fields.year.valueOf",
+ "call fields.calendar.dateFromFields",
+ // inside Calendar.p.dateFromFields
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "fields.calendar");
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ year: 1.7,
+ month: 1.7,
+ monthCode: "M01",
+ day: 1.7,
+ calendar,
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ overflow: "constrain",
+ extra: "property",
+}, "options");
+
+const result = Temporal.PlainDate.from(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/overflow-invalid-string.js
new file mode 100644
index 0000000000..29655c52be
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/overflow-invalid-string.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-totemporaldate steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ g. Return ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalDate]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalDate(_item_, _options_).
+features: [Temporal]
+---*/
+
+const validItems = [
+ new Temporal.PlainDate(2000, 5, 2),
+ new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"),
+ new Temporal.PlainDateTime(2000, 5, 2, 12),
+ { year: 2000, month: 5, day: 2 },
+ "2000-05-02",
+];
+
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const item of validItems) {
+ for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.from(item, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/overflow-undefined.js
new file mode 100644
index 0000000000..08b28cb336
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/overflow-undefined.js
@@ -0,0 +1,48 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-totemporaldate steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ g. Return ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalDate]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalDate(_item_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainDate(2000, 5, 2),
+ "2000-05-02",
+];
+validValues.forEach((value) => {
+ const explicit = Temporal.PlainDate.from(value, { overflow: undefined });
+ TemporalHelpers.assertPlainDate(explicit, 2000, 5, "M05", 2, "overflow is ignored");
+ const implicit = Temporal.PlainDate.from(value, {});
+ TemporalHelpers.assertPlainDate(implicit, 2000, 5, "M05", 2, "overflow is ignored");
+ const lambda = Temporal.PlainDate.from(value, () => {});
+ TemporalHelpers.assertPlainDate(lambda, 2000, 5, "M05", 2, "overflow is ignored");
+});
+
+const propertyBag = { year: 2000, month: 13, day: 34 };
+const explicit = Temporal.PlainDate.from(propertyBag, { overflow: undefined });
+TemporalHelpers.assertPlainDate(explicit, 2000, 12, "M12", 31, "default overflow is constrain");
+const implicit = Temporal.PlainDate.from(propertyBag, {});
+TemporalHelpers.assertPlainDate(implicit, 2000, 12, "M12", 31, "default overflow is constrain");
+const lambda = Temporal.PlainDate.from(propertyBag, () => {});
+TemporalHelpers.assertPlainDate(lambda, 2000, 12, "M12", 31, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/overflow-wrong-type.js
new file mode 100644
index 0000000000..8774278ffd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/overflow-wrong-type.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-totemporaldate steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ g. Return ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalDate]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalDate(_item_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainDate(2000, 5, 2),
+ { year: 2000, month: 5, day: 2 },
+ "2000-05-02",
+];
+validValues.forEach((value) => TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => Temporal.PlainDate.from(value, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 2, descr),
+));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/prop-desc.js
new file mode 100644
index 0000000000..0664d2fe4c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: The "from" property of Temporal.PlainDate
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.from,
+ "function",
+ "`typeof PlainDate.from` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate, "from", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/subclassing-ignored.js
new file mode 100644
index 0000000000..637f254848
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/subclassing-ignored.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: The receiver is never called when calling from()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.PlainDate,
+ "from",
+ ["2000-05-02"],
+ (result) => TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 2),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/year-zero.js
new file mode 100644
index 0000000000..6bd3006711
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/year-zero.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.from(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..33355c37ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/infinity-throws-rangeerror.js
@@ -0,0 +1,42 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainDate throws a RangeError if any value is Infinity
+esid: sec-temporal.plaindate
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => new Temporal.PlainDate(Infinity, 1, 1));
+assert.throws(RangeError, () => new Temporal.PlainDate(1970, Infinity, 1));
+assert.throws(RangeError, () => new Temporal.PlainDate(1970, 1, Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite year",
+ [O(Infinity, "year"), O(1, "month"), O(1, "day")],
+ ["get year.valueOf", "call year.valueOf"]
+ ],
+ [
+ "infinite month",
+ [O(2, "year"), O(Infinity, "month"), O(1, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf"]
+ ],
+ [
+ "infinite day",
+ [O(2, "year"), O(1, "month"), O(Infinity, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf", "get day.valueOf", "call day.valueOf"]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.PlainDate(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/length.js
new file mode 100644
index 0000000000..bd19e68bae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate
+description: Temporal.PlainDate.length is 3
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate, "length", {
+ value: 3,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/limits.js
new file mode 100644
index 0000000000..10fb6a5e24
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/limits.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate
+description: Limits for the PlainDate constructor.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => new Temporal.PlainDate(-271821, 4, 18), "min");
+assert.throws(RangeError, () => new Temporal.PlainDate(275760, 9, 14), "max");
+TemporalHelpers.assertPlainDate(new Temporal.PlainDate(-271821, 4, 19),
+ -271821, 4, "M04", 19, "min");
+TemporalHelpers.assertPlainDate(new Temporal.PlainDate(275760, 9, 13),
+ 275760, 9, "M09", 13, "max");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/missing-arguments.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/missing-arguments.js
new file mode 100644
index 0000000000..86a92f87ef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/missing-arguments.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate
+description: RangeError thrown when constructor invoked with no argument
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "valueOf year",
+ "valueOf month",
+];
+const actual = [];
+const args = [
+ { valueOf() { actual.push("valueOf year"); return 1; } },
+ { valueOf() { actual.push("valueOf month"); return 1; } },
+];
+
+assert.throws(RangeError, () => new Temporal.PlainDate(...args));
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/name.js
new file mode 100644
index 0000000000..f5d4ba06ae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate
+description: Temporal.PlainDate.name is "PlainDate"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate, "name", {
+ value: "PlainDate",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..159ce4b9ed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,42 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainDate throws a RangeError if any value is -Infinity
+esid: sec-temporal.plaindate
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => new Temporal.PlainDate(-Infinity, 1, 1));
+assert.throws(RangeError, () => new Temporal.PlainDate(1970, -Infinity, 1));
+assert.throws(RangeError, () => new Temporal.PlainDate(1970, 1, -Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite year",
+ [O(-Infinity, "year"), O(1, "month"), O(1, "day")],
+ ["get year.valueOf", "call year.valueOf"]
+ ],
+ [
+ "infinite month",
+ [O(2, "year"), O(-Infinity, "month"), O(1, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf"]
+ ],
+ [
+ "infinite day",
+ [O(2, "year"), O(1, "month"), O(-Infinity, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf", "get day.valueOf", "call day.valueOf"]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.PlainDate(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prop-desc.js
new file mode 100644
index 0000000000..b8c8daf832
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate
+description: The "PlainDate" property of Temporal
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate,
+ "function",
+ "`typeof PlainDate` is `function`"
+);
+
+verifyProperty(Temporal, "PlainDate", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-duration-max.js
new file mode 100644
index 0000000000..22e0436354
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-duration-max.js
@@ -0,0 +1,58 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: Maximum allowed duration
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1970, 1, 1);
+
+const maxCases = [
+ ["P273790Y8M12DT23H59M59.999999999S", "string with max years"],
+ [{ years: 273790, months: 8, days: 12, nanoseconds: 86399999999999 }, "property bag with max years"],
+ ["P3285488M12DT23H59M59.999999999S", "string with max months"],
+ [{ months: 3285488, days: 12, nanoseconds: 86399999999999 }, "property bag with max months"],
+ ["P14285714W2DT23H59M59.999999999S", "string with max weeks"],
+ [{ weeks: 14285714, days: 2, nanoseconds: 86399999999999 }, "property bag with max weeks"],
+ ["P100000000DT23H59M59.999999999S", "string with max days"],
+ [{ days: 100000000, nanoseconds: 86399999999999 }, "property bag with max days"],
+ ["PT2400000023H59M59.999999999S", "string with max hours"],
+ [{ hours: 2400000023, nanoseconds: 3599999999999 }, "property bag with max hours"],
+ ["PT144000001439M59.999999999S", "string with max minutes"],
+ [{ minutes: 144000001439, nanoseconds: 59999999999 }, "property bag with max minutes"],
+ ["PT8640000086399.999999999S", "string with max seconds"],
+ [{ seconds: 8640000086399, nanoseconds: 999999999 }, "property bag with max seconds"],
+];
+
+for (const [arg, descr] of maxCases) {
+ const result = instance.add(arg);
+ TemporalHelpers.assertPlainDate(result, 275760, 9, "M09", 13, `operation succeeds with ${descr}`);
+}
+
+const minCases = [
+ ["-P273790Y8M12DT23H59M59.999999999S", "string with min years"],
+ [{ years: -273790, months: -8, days: -12, nanoseconds: -86399999999999 }, "property bag with min years"],
+ ["-P3285488M12DT23H59M59.999999999S", "string with min months"],
+ [{ months: -3285488, days: -12, nanoseconds: -86399999999999 }, "property bag with min months"],
+ ["-P14285714W3DT23H59M59.999999999S", "string with min weeks"],
+ [{ weeks: -14285714, days: -3, nanoseconds: -86399999999999 }, "property bag with min weeks"],
+ ["-P100000001DT23H59M59.999999999S", "string with min days"],
+ [{ days: -100000001, nanoseconds: -86399999999999 }, "property bag with min days"],
+ ["-PT2400000047H59M59.999999999S", "string with min hours"],
+ [{ hours: -2400000047, nanoseconds: -3599999999999 }, "property bag with min hours"],
+ ["-PT144000002879M59.999999999S", "string with min minutes"],
+ [{ minutes: -144000002879, nanoseconds: -59999999999 }, "property bag with min minutes"],
+ ["-PT8640000172799.999999999S", "string with min seconds"],
+ [{ seconds: -8640000172799, nanoseconds: -999999999 }, "property bag with min seconds"],
+];
+
+for (const [arg, descr] of minCases) {
+ const result = instance.add(arg);
+ TemporalHelpers.assertPlainDate(result, -271821, 4, "M04", 19, `operation succeeds with ${descr}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-duration-out-of-range.js
new file mode 100644
index 0000000000..6067a9e27c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-duration-out-of-range.js
@@ -0,0 +1,75 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: Duration-like argument that is out of range
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1970, 1, 1);
+
+const cases = [
+ // 2^32 = 4294967296
+ ["P4294967296Y", "string with years > max"],
+ [{ years: 4294967296 }, "property bag with years > max"],
+ ["-P4294967296Y", "string with years < min"],
+ [{ years: -4294967296 }, "property bag with years < min"],
+ ["P4294967296M", "string with months > max"],
+ [{ months: 4294967296 }, "property bag with months > max"],
+ ["-P4294967296M", "string with months < min"],
+ [{ months: -4294967296 }, "property bag with months < min"],
+ ["P4294967296W", "string with weeks > max"],
+ [{ weeks: 4294967296 }, "property bag with weeks > max"],
+ ["-P4294967296W", "string with weeks < min"],
+ [{ weeks: -4294967296 }, "property bag with weeks < min"],
+
+ // ceil(max safe integer / 86400) = 104249991375
+ ["P104249991375D", "string with days > max"],
+ [{ days: 104249991375 }, "property bag with days > max"],
+ ["P104249991374DT24H", "string where hours balance into days > max"],
+ [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"],
+ ["-P104249991375D", "string with days < min"],
+ [{ days: -104249991375 }, "property bag with days < min"],
+ ["-P104249991374DT24H", "string where hours balance into days < min"],
+ [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"],
+
+ // ceil(max safe integer / 3600) = 2501999792984
+ ["PT2501999792984H", "string with hours > max"],
+ [{ hours: 2501999792984 }, "property bag with hours > max"],
+ ["PT2501999792983H60M", "string where minutes balance into hours > max"],
+ [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"],
+ ["-PT2501999792984H", "string with hours < min"],
+ [{ hours: -2501999792984 }, "property bag with hours < min"],
+ ["-PT2501999792983H60M", "string where minutes balance into hours < min"],
+ [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"],
+
+ // ceil(max safe integer / 60) = 150119987579017
+ ["PT150119987579017M", "string with minutes > max"],
+ [{ minutes: 150119987579017 }, "property bag with minutes > max"],
+ ["PT150119987579016M60S", "string where seconds balance into minutes > max"],
+ [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"],
+ ["-PT150119987579017M", "string with minutes < min"],
+ [{ minutes: -150119987579017 }, "property bag with minutes < min"],
+ ["-PT150119987579016M60S", "string where seconds balance into minutes < min"],
+ [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"],
+
+ // 2^53 = 9007199254740992
+ ["PT9007199254740992S", "string with seconds > max"],
+ [{ seconds: 9007199254740992 }, "property bag with seconds > max"],
+ [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"],
+ ["-PT9007199254740992S", "string with seconds < min"],
+ [{ seconds: -9007199254740992 }, "property bag with seconds < min"],
+ [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"],
+];
+
+for (const [arg, descr] of cases) {
+ assert.throws(RangeError, () => instance.add(arg), `${descr} is out of range`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-invalid-property.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-invalid-property.js
new file mode 100644
index 0000000000..af962db8fb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-invalid-property.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: temporalDurationLike object must contain at least one correctly spelled property
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+assert.throws(
+ TypeError,
+ () => instance.add({}),
+ "Throws TypeError if no property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.add({ nonsense: true }),
+ "Throws TypeError if no recognized property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.add({ sign: 1 }),
+ "Sign property is not recognized"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-mixed-sign.js
new file mode 100644
index 0000000000..d5de33beb4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-mixed-sign.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: Positive and negative values in the temporalDurationLike argument are not acceptable
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+["constrain", "reject"].forEach((overflow) => {
+ assert.throws(
+ RangeError,
+ () => instance.add({ hours: 1, minutes: -30 }, { overflow }),
+ `mixed positive and negative values always throw (overflow = "${overflow}")`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-not-object.js
new file mode 100644
index 0000000000..06546bf79e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-not-object.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: Passing a primitive other than string to add() throws
+features: [Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+assert.throws(TypeError, () => instance.add(undefined), "undefined");
+assert.throws(TypeError, () => instance.add(null), "null");
+assert.throws(TypeError, () => instance.add(true), "boolean");
+assert.throws(RangeError, () => instance.add(""), "empty string");
+assert.throws(TypeError, () => instance.add(Symbol()), "Symbol");
+assert.throws(TypeError, () => instance.add(7), "number");
+assert.throws(TypeError, () => instance.add(7n), "bigint");
+assert.throws(TypeError, () => instance.add([]), "array");
+assert.throws(TypeError, () => instance.add(() => {}), "function");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-singular-properties.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-singular-properties.js
new file mode 100644
index 0000000000..d37504ccc6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-singular-properties.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: Singular properties in the property bag are always ignored
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+[
+ { year: 1 },
+ { month: 2 },
+ { week: 3 },
+ { day: 4 },
+ { hour: 5 },
+ { minute: 6 },
+ { second: 7 },
+ { millisecond: 8 },
+ { microsecond: 9 },
+ { nanosecond: 10 },
+].forEach((badObject) => {
+ assert.throws(TypeError, () => instance.add(badObject),
+ "Throw TypeError if temporalDurationLike is not valid");
+});
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-string-negative-fractional-units.js
new file mode 100644
index 0000000000..b0dc6ef7f3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-string-negative-fractional-units.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const resultHours = instance.add("-PT24.567890123H");
+TemporalHelpers.assertPlainDate(resultHours, 2000, 5, "M05", 1, "negative fractional hours");
+
+const resultMinutes = instance.add("-PT1440.567890123M");
+TemporalHelpers.assertPlainDate(resultMinutes, 2000, 5, "M05", 1, "negative fractional minutes");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-string.js
new file mode 100644
index 0000000000..4155fc39a4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-string.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: A string is parsed into the correct object when passed as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = Temporal.PlainDate.from({ year: 2000, month: 5, day: 2 });
+const result = instance.add("P3D");
+TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 5);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/balance-smaller-units-basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/balance-smaller-units-basic.js
new file mode 100644
index 0000000000..b5f43668b0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/balance-smaller-units-basic.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: Durations with units smaller than days are balanced
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(1976, 11, 18);
+
+// lower units that don't balance up to a day
+TemporalHelpers.assertPlainDate(date.add({ hours: 1 }), 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(date.add({ minutes: 1 }), 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(date.add({ seconds: 1 }), 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(date.add({ milliseconds: 1 }), 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(date.add({ microseconds: 1 }), 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(date.add({ nanoseconds: 1 }), 1976, 11, "M11", 18);
+
+// lower units that balance up to a day or more
+TemporalHelpers.assertPlainDate(date.add({ hours: 24 }), 1976, 11, "M11", 19);
+TemporalHelpers.assertPlainDate(date.add({ hours: 36 }), 1976, 11, "M11", 19);
+TemporalHelpers.assertPlainDate(date.add({ hours: 48 }), 1976, 11, "M11", 20);
+TemporalHelpers.assertPlainDate(date.add({ minutes: 1440 }), 1976, 11, "M11", 19);
+TemporalHelpers.assertPlainDate(date.add({ seconds: 86400 }), 1976, 11, "M11", 19);
+TemporalHelpers.assertPlainDate(date.add({ milliseconds: 86400_000 }), 1976, 11, "M11", 19);
+TemporalHelpers.assertPlainDate(date.add({ microseconds: 86400_000_000 }), 1976, 11, "M11", 19);
+TemporalHelpers.assertPlainDate(date.add({ nanoseconds: 86400_000_000_000 }), 1976, 11, "M11", 19);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/balance-smaller-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/balance-smaller-units.js
new file mode 100644
index 0000000000..c9d42a3b87
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/balance-smaller-units.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: Durations with units smaller than days are balanced before adding, in the calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const duration = new Temporal.Duration(0, 0, 0, 1, 24, 1440, 86400, 86400_000, 86400_000_000, 86400_000_000_000);
+
+const result = date.add(duration);
+TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 9, "units smaller than days are balanced");
+
+const resultString = date.add("P1DT24H1440M86400S");
+TemporalHelpers.assertPlainDate(resultString, 2000, 5, "M05", 6, "units smaller than days are balanced");
+
+const resultPropBag = date.add({ days: 1, hours: 24, minutes: 1440, seconds: 86400, milliseconds: 86400_000, microseconds: 86400_000_000, nanoseconds: 86400_000_000_000 });
+TemporalHelpers.assertPlainDate(resultPropBag, 2000, 5, "M05", 9, "units smaller than days are balanced");
+
+const negativeDuration = new Temporal.Duration(0, 0, 0, -1, -24, -1440, -86400, -86400_000, -86400_000_000, -86400_000_000_000);
+const resultNegative = date.add(negativeDuration);
+TemporalHelpers.assertPlainDate(resultNegative, 2000, 4, "M04", 25, "units smaller than days are balanced");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/basic.js
new file mode 100644
index 0000000000..0d757e7129
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/basic.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: Basic tests
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = Temporal.PlainDate.from("1976-11-18");
+TemporalHelpers.assertPlainDate(date.add({ years: 43 }),
+ 2019, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(date.add({ months: 3 }),
+ 1977, 2, "M02", 18);
+TemporalHelpers.assertPlainDate(date.add({ days: 20 }),
+ 1976, 12, "M12", 8);
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from("2019-01-31").add({ months: 1 }),
+ 2019, 2, "M02", 28);
+TemporalHelpers.assertPlainDate(date.add(Temporal.Duration.from('P43Y')),
+ 2019, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from('2019-11-18').add({ years: -43 }),
+ 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from('1977-02-18').add({ months: -3 }),
+ 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from('1976-12-08').add({ days: -20 }),
+ 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from('2019-02-28').add({ months: -1 }),
+ 2019, 1, "M01", 28);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/branding.js
new file mode 100644
index 0000000000..111bc9929c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const add = Temporal.PlainDate.prototype.add;
+
+assert.sameValue(typeof add, "function");
+
+const args = [new Temporal.Duration(5)];
+
+assert.throws(TypeError, () => add.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => add.apply(null, args), "null");
+assert.throws(TypeError, () => add.apply(true, args), "true");
+assert.throws(TypeError, () => add.apply("", args), "empty string");
+assert.throws(TypeError, () => add.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => add.apply(1, args), "1");
+assert.throws(TypeError, () => add.apply({}, args), "plain object");
+assert.throws(TypeError, () => add.apply(Temporal.PlainDate, args), "Temporal.PlainDate");
+assert.throws(TypeError, () => add.apply(Temporal.PlainDate.prototype, args), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..66838eb6e1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateAddOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateAdd");
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateAdd should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.add(new Temporal.Duration(1));
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", dateAddOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/builtin.js
new file mode 100644
index 0000000000..2ab3bd0c98
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: >
+ Tests that Temporal.PlainDate.prototype.add
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.add),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.add),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.add),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.add.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/calendar-invalid-return.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/calendar-invalid-return.js
new file mode 100644
index 0000000000..0cbfb34881
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/calendar-invalid-return.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: Throw when the returned value from the calendar's dateAdd method is not a PlainDate.
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor(value) {
+ super("iso8601");
+ this.value = value;
+ }
+ dateAdd() {
+ return this.value;
+ }
+}
+
+const tests = [
+ [undefined],
+ [null, "null"],
+ [true],
+ ["2000-05"],
+ [Symbol()],
+ [200005],
+ [200005n],
+ [{}, "plain object"],
+ [() => {}, "lambda"],
+ [Temporal.PlainDate, "Temporal.PlainDate"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype"],
+];
+for (const [test, description = typeof test] of tests) {
+ const plainDate = new Temporal.PlainDate(2000, 5, 2, new CustomCalendar(test));
+ assert.throws(TypeError, () => plainDate.add({ years: 1 }), `Expected error with ${description}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/custom.js
new file mode 100644
index 0000000000..f220ad6225
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/custom.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: Basic tests with custom calendar
+includes: [compareArray.js,temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const result = new Temporal.PlainDate(1920, 5, 3);
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(...args) {
+ ++calls;
+ assert.sameValue(args.length, 3, "Three arguments");
+ assert.sameValue(args[0], plainDate, "First argument");
+ TemporalHelpers.assertDuration(args[1], 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Second argument");
+ assert.sameValue(typeof args[2], "object", "Third argument: type");
+ assert.sameValue(Object.getPrototypeOf(args[2]), null, "Third argument: prototype");
+ assert.compareArray(Object.keys(args[2]), [], "Third argument: keys");
+ return result;
+ }
+}
+const calendar = new CustomCalendar();
+const plainDate = new Temporal.PlainDate(1976, 11, 18, calendar);
+assert.sameValue(plainDate.add({ years: 43 }), result);
+assert.sameValue(calls, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..c68829b5a2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainDate.prototype.add throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.plaindate.prototype.add
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainDate.from({ year: 2000, month: 5, day: 2 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/length.js
new file mode 100644
index 0000000000..d5031ff92e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: Temporal.PlainDate.prototype.add.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.add, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/limits.js
new file mode 100644
index 0000000000..dedd779131
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/limits.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainDate.prototype.add throws a RangeError if the calculation crosses a limit
+esid: sec-temporal.plaindate.prototype.add
+features: [Temporal]
+---*/
+
+const min = Temporal.PlainDate.from("-271821-04-19");
+const max = Temporal.PlainDate.from("+275760-09-13");
+["reject", "constrain"].forEach((overflow) => {
+ assert.throws(RangeError, () => min.add({ days: -1 }, { overflow }), `min with ${overflow}`);
+ assert.throws(RangeError, () => max.add({ days: 1 }, { overflow }), `max with ${overflow}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/name.js
new file mode 100644
index 0000000000..21e3495429
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: Temporal.PlainDate.prototype.add.name is "add".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.add, "name", {
+ value: "add",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..44014165ee
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainDate.prototype.add throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.plaindate.prototype.add
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainDate.from({ year: 2000, month: 5, day: 2 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: -Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/non-integer-throws-rangeerror.js
new file mode 100644
index 0000000000..1dd6d9b79b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/non-integer-throws-rangeerror.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.add({ [field]: -1.5 }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/not-a-constructor.js
new file mode 100644
index 0000000000..4234996527
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: >
+ Temporal.PlainDate.prototype.add does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.add();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.add), false,
+ "isConstructor(Temporal.PlainDate.prototype.add)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/options-object.js
new file mode 100644
index 0000000000..a827bcd6e8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/options-object.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const result1 = instance.add({ months: 1 }, {});
+TemporalHelpers.assertPlainDate(
+ result1, 2000, 6, "M06", 2,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.add({ months: 1 }, () => {});
+TemporalHelpers.assertPlainDate(
+ result2, 2000, 6, "M06", 2,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/options-undefined.js
new file mode 100644
index 0000000000..914f728206
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/options-undefined.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 1, 31);
+const duration = { months: 1 };
+
+const explicit = date.add(duration, undefined);
+assert.sameValue(explicit.month, 2, "default overflow is constrain");
+assert.sameValue(explicit.day, 29, "default overflow is constrain");
+
+const implicit = date.add(duration);
+assert.sameValue(implicit.month, 2, "default overflow is constrain");
+assert.sameValue(implicit.day, 29, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/options-wrong-type.js
new file mode 100644
index 0000000000..a068e69773
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/options-wrong-type.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.add({ months: 1 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/order-of-operations.js
new file mode 100644
index 0000000000..d19a06209b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/order-of-operations.js
@@ -0,0 +1,129 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: Properties on an object passed to add() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalDurationRecord
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.months.valueOf",
+ "call fields.months.valueOf",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.weeks.valueOf",
+ "call fields.weeks.valueOf",
+ "get fields.years",
+ "get fields.years.valueOf",
+ "call fields.years.valueOf",
+ // AddDate
+ "get this.calendar.dateAdd",
+ "call this.calendar.dateAdd",
+ // inside Calendar.p.dateAdd
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ overflow: "constrain",
+}, "options");
+
+instance.add(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+actual.splice(0); // clear
+
+const noCalendarExpected = [
+ // ToTemporalDurationRecord
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.years",
+ "get this.calendar.dateAdd",
+ // AddDate
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+
+const noCalendarFields = TemporalHelpers.propertyBagObserver(actual, {
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+instance.add(noCalendarFields, options);
+assert.compareArray(actual, noCalendarExpected, "order of operations with no calendar operation");
+
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-constrain.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-constrain.js
new file mode 100644
index 0000000000..6bf13939f1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-constrain.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: Constrains with overflow constrain
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const jan31 = Temporal.PlainDate.from("2020-01-31");
+TemporalHelpers.assertPlainDate(jan31.add({ months: 1 }, { overflow: "constrain" }),
+ 2020, 2, "M02", 29);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-invalid-string.js
new file mode 100644
index 0000000000..0853583603
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-invalid-string.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.prototype.add step 7:
+ 7. Return ? CalendarDateAdd(_temporalDate_.[[Calendar]], _temporalDate_, _balancedDuration_, _options_).
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const duration = new Temporal.Duration(3, 3, 0, 3);
+
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => date.add(duration, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-reject.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-reject.js
new file mode 100644
index 0000000000..aa9672d377
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-reject.js
@@ -0,0 +1,14 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: Throws with overflow reject
+features: [Temporal]
+---*/
+
+const jan31 = Temporal.PlainDate.from("2020-01-31");
+assert.throws(RangeError, () => jan31.add({ months: 1 }, { overflow: "reject" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-undefined.js
new file mode 100644
index 0000000000..6f923341f3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-undefined.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.prototype.add step 7:
+ 7. Return ? CalendarDateAdd(_temporalDate_.[[Calendar]], _temporalDate_, _balancedDuration_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 31);
+const duration = new Temporal.Duration(3, 1);
+
+const explicit = date.add(duration, { overflow: undefined });
+TemporalHelpers.assertPlainDate(explicit, 2003, 6, "M06", 30, "default overflow is constrain");
+const implicit = date.add(duration, {});
+TemporalHelpers.assertPlainDate(implicit, 2003, 6, "M06", 30, "default overflow is constrain");
+const lambda = date.add(duration, () => {});
+TemporalHelpers.assertPlainDate(lambda, 2003, 6, "M06", 30, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-wrong-type.js
new file mode 100644
index 0000000000..98927d249e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-wrong-type.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.prototype.add step 7:
+ 7. Return ? CalendarDateAdd(_temporalDate_.[[Calendar]], _temporalDate_, _balancedDuration_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const duration = new Temporal.Duration(3, 3, 0, 3);
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => date.add(duration, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainDate(result, 2003, 8, "M08", 5, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/prop-desc.js
new file mode 100644
index 0000000000..8bb7789f01
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: The "add" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.add,
+ "function",
+ "`typeof PlainDate.prototype.add` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "add", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/subclassing-ignored.js
new file mode 100644
index 0000000000..abbe09f5df
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/subclassing-ignored.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: Objects of a subclass are never created as return values for add()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDate,
+ [2000, 5, 2],
+ "add",
+ [{ days: 1 }],
+ (result) => TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 3),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/branding.js
new file mode 100644
index 0000000000..87e61c2db5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.calendarid
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const calendarId = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "calendarId").get;
+
+assert.sameValue(typeof calendarId, "function");
+
+assert.throws(TypeError, () => calendarId.call(undefined), "undefined");
+assert.throws(TypeError, () => calendarId.call(null), "null");
+assert.throws(TypeError, () => calendarId.call(true), "true");
+assert.throws(TypeError, () => calendarId.call(""), "empty string");
+assert.throws(TypeError, () => calendarId.call(Symbol()), "symbol");
+assert.throws(TypeError, () => calendarId.call(1), "1");
+assert.throws(TypeError, () => calendarId.call({}), "plain object");
+assert.throws(TypeError, () => calendarId.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => calendarId.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..c4c44332c6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.calendarid
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.calendarId;
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/prop-desc.js
new file mode 100644
index 0000000000..bea8a35cce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.calendarid
+description: The "calendarId" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "calendarId");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/constructor.js
new file mode 100644
index 0000000000..3363c592ed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/constructor.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.constructor
+description: Test for Temporal.PlainDate.prototype.constructor.
+info: The initial value of Temporal.PlainDate.prototype.constructor is %Temporal.PlainDate%.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype, "constructor", {
+ value: Temporal.PlainDate,
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/branding.js
new file mode 100644
index 0000000000..307ed9319c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.day
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const day = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "day").get;
+
+assert.sameValue(typeof day, "function");
+
+assert.throws(TypeError, () => day.call(undefined), "undefined");
+assert.throws(TypeError, () => day.call(null), "null");
+assert.throws(TypeError, () => day.call(true), "true");
+assert.throws(TypeError, () => day.call(""), "empty string");
+assert.throws(TypeError, () => day.call(Symbol()), "symbol");
+assert.throws(TypeError, () => day.call(1), "1");
+assert.throws(TypeError, () => day.call({}), "plain object");
+assert.throws(TypeError, () => day.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => day.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..f80dbba88e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.day
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dayOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "day");
+Object.defineProperty(Temporal.Calendar.prototype, "day", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("day should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.day;
+
+Object.defineProperty(Temporal.Calendar.prototype, "day", dayOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/custom.js
new file mode 100644
index 0000000000..c74d2b56c4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.day
+description: Custom calendar tests for day().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ day(...args) {
+ ++calls;
+ assert.compareArray(args, [pd], "day arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pd = new Temporal.PlainDate(1830, 8, 25, calendar);
+const result = pd.day;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/prop-desc.js
new file mode 100644
index 0000000000..a51472e4cd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.day
+description: The "day" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "day");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/validate-calendar-value.js
new file mode 100644
index 0000000000..3cecbfd3a5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/validate-calendar-value.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.day
+description: Validate result returned from calendar day() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ day() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.throws(error, () => instance.day, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/basic.js
new file mode 100644
index 0000000000..e87712e78a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/basic.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.dayofweek
+description: Basic tests for dayOfWeek().
+features: [Temporal]
+---*/
+
+for (let i = 1; i <= 7; ++i) {
+ const plainDate = new Temporal.PlainDate(1976, 11, 14 + i);
+ assert.sameValue(plainDate.dayOfWeek, i, `${plainDate} should be on day ${i}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/branding.js
new file mode 100644
index 0000000000..24f4f288ad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.dayofweek
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const dayOfWeek = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "dayOfWeek").get;
+
+assert.sameValue(typeof dayOfWeek, "function");
+
+assert.throws(TypeError, () => dayOfWeek.call(undefined), "undefined");
+assert.throws(TypeError, () => dayOfWeek.call(null), "null");
+assert.throws(TypeError, () => dayOfWeek.call(true), "true");
+assert.throws(TypeError, () => dayOfWeek.call(""), "empty string");
+assert.throws(TypeError, () => dayOfWeek.call(Symbol()), "symbol");
+assert.throws(TypeError, () => dayOfWeek.call(1), "1");
+assert.throws(TypeError, () => dayOfWeek.call({}), "plain object");
+assert.throws(TypeError, () => dayOfWeek.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => dayOfWeek.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..06c14532d2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.dayofweek
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dayOfWeekOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dayOfWeek");
+Object.defineProperty(Temporal.Calendar.prototype, "dayOfWeek", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dayOfWeek should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.dayOfWeek;
+
+Object.defineProperty(Temporal.Calendar.prototype, "dayOfWeek", dayOfWeekOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/custom.js
new file mode 100644
index 0000000000..1f3ad60d72
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.dayofweek
+description: Custom calendar tests for dayOfWeek().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dayOfWeek(...args) {
+ ++calls;
+ assert.compareArray(args, [pd], "dayOfWeek arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pd = new Temporal.PlainDate(1830, 8, 25, calendar);
+const result = pd.dayOfWeek;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/prop-desc.js
new file mode 100644
index 0000000000..51629ce17f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.dayofweek
+description: The "dayOfWeek" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "dayOfWeek");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/validate-calendar-value.js
new file mode 100644
index 0000000000..b3a5583270
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/validate-calendar-value.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.dayofweek
+description: Validate result returned from calendar dayOfWeek() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ dayOfWeek() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.throws(error, () => instance.dayOfWeek, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/basic.js
new file mode 100644
index 0000000000..a0b4fc9967
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/basic.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.dayofyear
+description: Basic tests for dayOfYear().
+features: [Temporal]
+---*/
+
+for (let i = 1; i <= 7; ++i) {
+ const plainDate = new Temporal.PlainDate(1976, 11, 14 + i);
+ assert.sameValue(plainDate.dayOfYear, 319 + i, `${plainDate} should be on day ${319 + i}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/branding.js
new file mode 100644
index 0000000000..b9de274662
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.dayofyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const dayOfYear = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "dayOfYear").get;
+
+assert.sameValue(typeof dayOfYear, "function");
+
+assert.throws(TypeError, () => dayOfYear.call(undefined), "undefined");
+assert.throws(TypeError, () => dayOfYear.call(null), "null");
+assert.throws(TypeError, () => dayOfYear.call(true), "true");
+assert.throws(TypeError, () => dayOfYear.call(""), "empty string");
+assert.throws(TypeError, () => dayOfYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => dayOfYear.call(1), "1");
+assert.throws(TypeError, () => dayOfYear.call({}), "plain object");
+assert.throws(TypeError, () => dayOfYear.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => dayOfYear.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..b07a147c44
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.dayofyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dayOfYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dayOfYear");
+Object.defineProperty(Temporal.Calendar.prototype, "dayOfYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dayOfYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.dayOfYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "dayOfYear", dayOfYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/custom.js
new file mode 100644
index 0000000000..3dc1efba96
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.dayofyear
+description: Custom calendar tests for dayOfYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dayOfYear(...args) {
+ ++calls;
+ assert.compareArray(args, [pd], "dayOfYear arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pd = new Temporal.PlainDate(1830, 8, 25, calendar);
+const result = pd.dayOfYear;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/prop-desc.js
new file mode 100644
index 0000000000..789531add6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.dayofyear
+description: The "dayOfYear" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "dayOfYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/validate-calendar-value.js
new file mode 100644
index 0000000000..025b76dcdd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/validate-calendar-value.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.dayofyear
+description: Validate result returned from calendar dayOfYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ dayOfYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.throws(error, () => instance.dayOfYear, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/basic.js
new file mode 100644
index 0000000000..022b6d218a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/basic.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.daysinmonth
+description: Checking days in month for a "normal" case (non-undefined, non-boundary case, etc.)
+features: [Temporal]
+---*/
+
+const tests = [
+ [new Temporal.PlainDate(1976, 2, 18), 29],
+ [new Temporal.PlainDate(1976, 11, 18), 30],
+ [new Temporal.PlainDate(1976, 12, 18), 31],
+ [new Temporal.PlainDate(1977, 2, 18), 28],
+];
+for (const [plainDate, expected] of tests) {
+ assert.sameValue(plainDate.daysInMonth, expected, `${expected} days in the month of ${plainDate}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/branding.js
new file mode 100644
index 0000000000..a3650a8d64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.daysinmonth
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const daysInMonth = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "daysInMonth").get;
+
+assert.sameValue(typeof daysInMonth, "function");
+
+assert.throws(TypeError, () => daysInMonth.call(undefined), "undefined");
+assert.throws(TypeError, () => daysInMonth.call(null), "null");
+assert.throws(TypeError, () => daysInMonth.call(true), "true");
+assert.throws(TypeError, () => daysInMonth.call(""), "empty string");
+assert.throws(TypeError, () => daysInMonth.call(Symbol()), "symbol");
+assert.throws(TypeError, () => daysInMonth.call(1), "1");
+assert.throws(TypeError, () => daysInMonth.call({}), "plain object");
+assert.throws(TypeError, () => daysInMonth.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => daysInMonth.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..f9b7544a2c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.daysinmonth
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const daysInMonthOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "daysInMonth");
+Object.defineProperty(Temporal.Calendar.prototype, "daysInMonth", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("daysInMonth should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.daysInMonth;
+
+Object.defineProperty(Temporal.Calendar.prototype, "daysInMonth", daysInMonthOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/custom.js
new file mode 100644
index 0000000000..b53aee4e36
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.daysinmonth
+description: Custom calendar tests for daysInMonth().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ daysInMonth(...args) {
+ ++calls;
+ assert.compareArray(args, [pd], "daysInMonth arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pd = new Temporal.PlainDate(1830, 8, 25, calendar);
+const result = pd.daysInMonth;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/prop-desc.js
new file mode 100644
index 0000000000..00f8dc0aa0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.daysinmonth
+description: The "daysInMonth" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "daysInMonth");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/validate-calendar-value.js
new file mode 100644
index 0000000000..2c903a74ce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/validate-calendar-value.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.daysinmonth
+description: Validate result returned from calendar daysInMonth() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ daysInMonth() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.throws(error, () => instance.daysInMonth, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/basic.js
new file mode 100644
index 0000000000..8e470e1e42
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/basic.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.daysinweek
+description: Basic tests for daysInWeek().
+features: [Temporal]
+---*/
+
+const tests = [
+ new Temporal.PlainDate(1976, 1, 1),
+ new Temporal.PlainDate(1976, 11, 18),
+ new Temporal.PlainDate(1976, 12, 31),
+];
+for (const plainDate of tests) {
+ assert.sameValue(plainDate.daysInWeek, 7, `Seven days in the week of ${plainDate}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/branding.js
new file mode 100644
index 0000000000..6e010896dd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.daysinweek
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const daysInWeek = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "daysInWeek").get;
+
+assert.sameValue(typeof daysInWeek, "function");
+
+assert.throws(TypeError, () => daysInWeek.call(undefined), "undefined");
+assert.throws(TypeError, () => daysInWeek.call(null), "null");
+assert.throws(TypeError, () => daysInWeek.call(true), "true");
+assert.throws(TypeError, () => daysInWeek.call(""), "empty string");
+assert.throws(TypeError, () => daysInWeek.call(Symbol()), "symbol");
+assert.throws(TypeError, () => daysInWeek.call(1), "1");
+assert.throws(TypeError, () => daysInWeek.call({}), "plain object");
+assert.throws(TypeError, () => daysInWeek.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => daysInWeek.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..7eb50b35d9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.daysinweek
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const daysInWeekOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "daysInWeek");
+Object.defineProperty(Temporal.Calendar.prototype, "daysInWeek", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("daysInWeek should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.daysInWeek;
+
+Object.defineProperty(Temporal.Calendar.prototype, "daysInWeek", daysInWeekOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/custom.js
new file mode 100644
index 0000000000..3942f6a3af
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.daysinweek
+description: Custom calendar tests for daysInWeek().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ daysInWeek(...args) {
+ ++calls;
+ assert.compareArray(args, [pd], "daysInWeek arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pd = new Temporal.PlainDate(1830, 8, 25, calendar);
+const result = pd.daysInWeek;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/prop-desc.js
new file mode 100644
index 0000000000..8d04f5afe1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.daysinweek
+description: The "daysInWeek" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "daysInWeek");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/validate-calendar-value.js
new file mode 100644
index 0000000000..e9eefdba7d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/validate-calendar-value.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.daysinweek
+description: Validate result returned from calendar daysInWeek() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ daysInWeek() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.throws(error, () => instance.daysInWeek, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/basic.js
new file mode 100644
index 0000000000..719a2fd527
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/basic.js
@@ -0,0 +1,14 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.daysinyear
+description: Basic tests for daysInYear.
+features: [Temporal]
+---*/
+
+assert.sameValue((new Temporal.PlainDate(1976, 11, 18)).daysInYear, 366, "leap year");
+assert.sameValue((new Temporal.PlainDate(1977, 11, 18)).daysInYear, 365, "non-leap year");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/branding.js
new file mode 100644
index 0000000000..c9caa7a2d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.daysinyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const daysInYear = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "daysInYear").get;
+
+assert.sameValue(typeof daysInYear, "function");
+
+assert.throws(TypeError, () => daysInYear.call(undefined), "undefined");
+assert.throws(TypeError, () => daysInYear.call(null), "null");
+assert.throws(TypeError, () => daysInYear.call(true), "true");
+assert.throws(TypeError, () => daysInYear.call(""), "empty string");
+assert.throws(TypeError, () => daysInYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => daysInYear.call(1), "1");
+assert.throws(TypeError, () => daysInYear.call({}), "plain object");
+assert.throws(TypeError, () => daysInYear.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => daysInYear.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..e38db3ec88
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.daysinyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const daysInYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "daysInYear");
+Object.defineProperty(Temporal.Calendar.prototype, "daysInYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("daysInYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.daysInYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "daysInYear", daysInYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/custom.js
new file mode 100644
index 0000000000..e9311cf1db
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.daysinyear
+description: Custom calendar tests for daysInYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ daysInYear(...args) {
+ ++calls;
+ assert.compareArray(args, [pd], "daysInYear arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pd = new Temporal.PlainDate(1830, 8, 25, calendar);
+const result = pd.daysInYear;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/prop-desc.js
new file mode 100644
index 0000000000..1e23a84375
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.daysinyear
+description: The "daysInYear" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "daysInYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/validate-calendar-value.js
new file mode 100644
index 0000000000..3676842033
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/validate-calendar-value.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.daysinyear
+description: Validate result returned from calendar daysInYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ daysInYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.throws(error, () => instance.daysInYear, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..4ceb1ce39b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.equals(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..da799c2c7b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-calendar-datefromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.equals(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..190426203f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-constructor-in-calendar-fields.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+assert.throws(RangeError, () => instance.equals(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..d233f81391
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.PlainDate(2000, 5, 2);
+
+ assert.throws(RangeError, () => instance.equals(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-leap-second.js
new file mode 100644
index 0000000000..6a8b47c6f6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-leap-second.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: Leap second is a valid ISO string for PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2016, 12, 31);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.equals(arg);
+assert.sameValue(
+ result1,
+ true,
+ "leap second is a valid ISO string for PlainDate"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.equals(arg);
+assert.sameValue(
+ result2,
+ true,
+ "second: 60 is ignored in property bag for PlainDate"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-number.js
new file mode 100644
index 0000000000..ce8907a765
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-number.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.equals(arg),
+ 'Numbers cannot be used in place of an ISO string for PlainDate'
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-object-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-object-invalid.js
new file mode 100644
index 0000000000..3d78637c6d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-object-invalid.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: Appropriate error thrown when object argument is invalid
+features: [Temporal]
+---*/
+
+const instance = Temporal.PlainDate.from({ year: 2000, month: 5, day: 2 });
+
+// End up in ISODateFromFields with missing fields
+assert.throws(TypeError, () => instance.equals({}), "plain object");
+assert.throws(TypeError, () => instance.equals({ year: 1972, month: 7 }), "only year, month");
+assert.throws(TypeError, () => instance.equals({ year: 1972, month: 7 }), "only year, month");
+assert.throws(TypeError, () => instance.equals({ year: 1972, day: 7 }), "only year, day");
+assert.throws(TypeError, () => instance.equals(Temporal.PlainDate), "Temporal.PlainDate");
+
+// Tries to get fields with an invalid receiver
+assert.throws(TypeError, () => instance.equals(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-object-valid.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-object-valid.js
new file mode 100644
index 0000000000..635405aa14
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-object-valid.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: equals with a valid property bag
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+assert.sameValue(instance.equals({ year: 2000, month: 5, day: 2 }), true, "same date");
+assert.sameValue(instance.equals({ year: 2000, month: 5, day: 4 }), false, "different date");
+
+const calendar = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "a",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+assert.sameValue(instance.withCalendar(calendar).equals({ year: 2000, month: 5, day: 2 }),
+ false, "different calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-plaindatetime.js
new file mode 100644
index 0000000000..76e9e3464c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-plaindatetime.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainDate by reading internal slots
+info: |
+ sec-temporal.plaindate.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalDate(_other_).
+ sec-temporal-totemporaldate step 2.b:
+ b. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ i. Return ! CreateTemporalDate(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ assert(date.equals(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..294ce1d006
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.equals(arg);
+assert.sameValue(result, true, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..8182dd7587
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.equals(arg);
+assert.sameValue(
+ result,
+ true,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..9510983c61
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-number.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.equals(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..637ccc83eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-string.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.equals(arg);
+assert.sameValue(result, true, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..caacd9266a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.equals(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.equals(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..dc0a12c58a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-year-zero.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..5bc1294f59
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-proto-in-calendar-fields.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+assert.throws(RangeError, () => instance.equals(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..dfb9082b99
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-calendar-annotation.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..a8799d431c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!foo=bar]",
+ "1970-01-01T00:00[!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..7625140eec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-date-with-utc-offset.js
@@ -0,0 +1,48 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+const invalidStrings = [
+ "2022-09-15Z",
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-invalid.js
new file mode 100644
index 0000000000..f16df08054
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-invalid.js
@@ -0,0 +1,64 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `"${arg}" should not be a valid ISO string for a PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..6d02de0a76
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-multiple-calendar.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..f444e8bb2d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-multiple-time-zone.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[UTC][UTC]",
+ "1970-01-01T00:00[UTC][UTC]",
+ "1970-01-01T00:00[!UTC][UTC]",
+ "1970-01-01T00:00[UTC][!UTC]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00[UTC][foo=bar][UTC]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-time-separators.js
new file mode 100644
index 0000000000..cf9bd7cf12
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-time-separators.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..621e81d60a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-time-zone-annotation.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[Asia/Kolkata]", "named, with no time"],
+ ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"],
+ ["2000-05-02[+00:00]", "numeric, with no time"],
+ ["2000-05-02[!-02:30]", "numeric, with ! and no time"],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..9b669a031d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-unknown-annotation.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..483bc36245
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-with-utc-designator.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ "String with UTC designator should not be valid as a PlainDate"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string.js
new file mode 100644
index 0000000000..436d598fc1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: equals with a valid string
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+assert.sameValue(instance.equals("2000-05-02"), true, "same date");
+assert.sameValue(instance.equals("2000-05-04"), false, "different date");
+
+const calendar = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "a",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+assert.sameValue(instance.withCalendar(calendar).equals("2000-05-02"), false, "different calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-wrong-type.js
new file mode 100644
index 0000000000..214b01461d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-wrong-type.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => instance.equals(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.equals(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-convert.js
new file mode 100644
index 0000000000..5136721a65
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+assert.throws(Test262Error, () => instance.equals(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..ffee040bf0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-slots.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+const instance = new Temporal.PlainDate(1976, 11, 18);
+instance.equals(arg);
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..bd6dae260c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.equals(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..bc3c919fc5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => date.equals(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..94a7516198
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.equals(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..4d63297d7e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => date.equals(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/basic.js
new file mode 100644
index 0000000000..67aabdfb73
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/basic.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.protoype.equals
+description: basic tests
+features: [Temporal]
+---*/
+
+const date1 = new Temporal.PlainDate(1976, 11, 18);
+const date2 = new Temporal.PlainDate(1914, 2, 23);
+const date3 = new Temporal.PlainDate(1914, 2, 23);
+assert.sameValue(date1.equals(date2), false, "different dates");
+assert.sameValue(date2.equals(date3), true, "same dates");
+assert.sameValue(date2.equals(date2), true, "same objects");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/branding.js
new file mode 100644
index 0000000000..4cb44e59d3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const equals = Temporal.PlainDate.prototype.equals;
+
+assert.sameValue(typeof equals, "function");
+
+const args = [new Temporal.PlainDate(2022, 6, 22)];
+
+assert.throws(TypeError, () => equals.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => equals.apply(null, args), "null");
+assert.throws(TypeError, () => equals.apply(true, args), "true");
+assert.throws(TypeError, () => equals.apply("", args), "empty string");
+assert.throws(TypeError, () => equals.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => equals.apply(1, args), "1");
+assert.throws(TypeError, () => equals.apply({}, args), "plain object");
+assert.throws(TypeError, () => equals.apply(Temporal.PlainDate, args), "Temporal.PlainDate");
+assert.throws(TypeError, () => equals.apply(Temporal.PlainDate.prototype, args), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..542b98c5fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.equals(new Temporal.PlainDate(2000, 5, 2));
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/builtin.js
new file mode 100644
index 0000000000..84abf7bc2e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: >
+ Tests that Temporal.PlainDate.prototype.equals
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.equals),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.equals),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.equals),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.equals.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-call-different.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-call-different.js
new file mode 100644
index 0000000000..52352e6e43
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-call-different.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.protoype.equals
+description: test if the calendar is compared
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CalendarTraceId extends Temporal.Calendar {
+ constructor(id) {
+ super("iso8601");
+ this.id_ = id;
+ this.calls = 0;
+ }
+ get id() {
+ ++this.calls;
+ return this.id_;
+ }
+ toString() {
+ TemporalHelpers.assertUnreachable('toString should not be called');
+ }
+};
+
+const calendar1 = new CalendarTraceId("a");
+const date1 = new Temporal.PlainDate(1914, 2, 23, calendar1);
+
+const calendar2 = new CalendarTraceId("b");
+const date2 = new Temporal.PlainDate(1914, 2, 23, calendar2);
+
+assert.sameValue(date1.equals(date2), false, "different calendars");
+assert.sameValue(calendar1.calls, 1, "calendar1 id getter calls");
+assert.sameValue(calendar2.calls, 1, "calendar2 id getter calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-call-same.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-call-same.js
new file mode 100644
index 0000000000..15f32174c4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-call-same.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.protoype.equals
+description: test if the calendar is compared
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CalendarTraceToString extends Temporal.Calendar {
+ constructor(id) {
+ super("iso8601");
+ this.id_ = id;
+ this.calls = 0;
+ }
+ get id() {
+ ++this.calls;
+ return this.id_;
+ }
+ toString() {
+ TemporalHelpers.assertUnreachable('toString should not be called');
+ }
+};
+
+const calendar1 = new CalendarTraceToString("a");
+const date1 = new Temporal.PlainDate(1914, 2, 23, calendar1);
+
+const calendar2 = new CalendarTraceToString("a");
+const date2 = new Temporal.PlainDate(1914, 2, 23, calendar2);
+
+assert.sameValue(date1.equals(date2), true, "same calendar id");
+assert.sameValue(calendar1.calls, 1, "calendar1 id getter calls");
+assert.sameValue(calendar2.calls, 1, "calendar2 id getter calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..bb9ddd5be6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-datefromfields-called-with-options-undefined.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+instance.equals({ year: 2000, month: 5, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-fields-iterable.js
new file mode 100644
index 0000000000..71b0cbeb7b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-fields-iterable.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindate.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalDate(_other_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const date = new Temporal.PlainDate(2000, 5, 2, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+date.equals({ year: 2005, month: 6, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-no-call.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-no-call.js
new file mode 100644
index 0000000000..533feb35e4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-no-call.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.protoype.equals
+description: test if the calendar is compared
+features: [Temporal]
+---*/
+
+class CalendarTraceToString extends Temporal.Calendar {
+ constructor(id) {
+ super("iso8601");
+ this.id_ = id;
+ this.calls = 0;
+ }
+ toString() {
+ ++this.calls;
+ return this.id_;
+ }
+};
+
+const calendar1 = new CalendarTraceToString("a");
+const date1 = new Temporal.PlainDate(1914, 2, 23, calendar1);
+
+const calendar2 = new CalendarTraceToString("b");
+const date2 = new Temporal.PlainDate(1914, 2, 22, calendar2);
+
+assert.sameValue(date1.equals(date2), false, "different ISO dates");
+assert.sameValue(calendar1.calls, 0, "calendar1 toString() calls");
+assert.sameValue(calendar2.calls, 0, "calendar2 toString() calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-temporal-object.js
new file mode 100644
index 0000000000..4ea36b8a70
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindate.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalDate(_other_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const date = new Temporal.PlainDate(2000, 5, 2, temporalObject);
+ date.equals({ year: 2005, month: 6, day: 2, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..fd8f9bcb1c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/infinity-throws-rangeerror.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindate.prototype.equals
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.equals({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.equals({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/length.js
new file mode 100644
index 0000000000..ba9ffa7e32
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: Temporal.PlainDate.prototype.equals.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.equals, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/name.js
new file mode 100644
index 0000000000..2e4a79abc7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: Temporal.PlainDate.prototype.equals.name is "equals".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.equals, "name", {
+ value: "equals",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/not-a-constructor.js
new file mode 100644
index 0000000000..c7fd5403e8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: >
+ Temporal.PlainDate.prototype.equals does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.equals();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.equals), false,
+ "isConstructor(Temporal.PlainDate.prototype.equals)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/prop-desc.js
new file mode 100644
index 0000000000..690c834d65
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: The "equals" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.equals,
+ "function",
+ "`typeof PlainDate.prototype.equals` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "equals", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/year-zero.js
new file mode 100644
index 0000000000..d62248d870
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/year-zero.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/branding.js
new file mode 100644
index 0000000000..d941757460
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.getcalendar
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const getCalendar = Temporal.PlainDate.prototype.getCalendar;
+
+assert.sameValue(typeof getCalendar, "function");
+
+assert.throws(TypeError, () => getCalendar.call(undefined), "undefined");
+assert.throws(TypeError, () => getCalendar.call(null), "null");
+assert.throws(TypeError, () => getCalendar.call(true), "true");
+assert.throws(TypeError, () => getCalendar.call(""), "empty string");
+assert.throws(TypeError, () => getCalendar.call(Symbol()), "symbol");
+assert.throws(TypeError, () => getCalendar.call(1), "1");
+assert.throws(TypeError, () => getCalendar.call({}), "plain object");
+assert.throws(TypeError, () => getCalendar.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => getCalendar.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/builtin.js
new file mode 100644
index 0000000000..31432c79ae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.getcalendar
+description: >
+ Tests that Temporal.PlainDate.prototype.getCalendar
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.getCalendar),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.getCalendar),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.getCalendar),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.getCalendar.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/length.js
new file mode 100644
index 0000000000..7ed0f11c4e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.getcalendar
+description: Temporal.PlainDate.prototype.getCalendar.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.getCalendar, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/name.js
new file mode 100644
index 0000000000..7a508ff62e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.getcalendar
+description: Temporal.PlainDate.prototype.getCalendar.name is "getCalendar".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.getCalendar, "name", {
+ value: "getCalendar",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/not-a-constructor.js
new file mode 100644
index 0000000000..2cd96bf644
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.getcalendar
+description: >
+ Temporal.PlainDate.prototype.getCalendar does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.getCalendar();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.getCalendar), false,
+ "isConstructor(Temporal.PlainDate.prototype.getCalendar)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/prop-desc.js
new file mode 100644
index 0000000000..7087f4a214
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.getcalendar
+description: The "getCalendar" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.getCalendar,
+ "function",
+ "`typeof PlainDate.prototype.getCalendar` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "getCalendar", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/branding.js
new file mode 100644
index 0000000000..54eb2801ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.getisofields
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const getISOFields = Temporal.PlainDate.prototype.getISOFields;
+
+assert.sameValue(typeof getISOFields, "function");
+
+assert.throws(TypeError, () => getISOFields.call(undefined), "undefined");
+assert.throws(TypeError, () => getISOFields.call(null), "null");
+assert.throws(TypeError, () => getISOFields.call(true), "true");
+assert.throws(TypeError, () => getISOFields.call(""), "empty string");
+assert.throws(TypeError, () => getISOFields.call(Symbol()), "symbol");
+assert.throws(TypeError, () => getISOFields.call(1), "1");
+assert.throws(TypeError, () => getISOFields.call({}), "plain object");
+assert.throws(TypeError, () => getISOFields.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => getISOFields.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/builtin.js
new file mode 100644
index 0000000000..6e413062fe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.getisofields
+description: >
+ Tests that Temporal.PlainDate.prototype.getISOFields
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.getISOFields),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.getISOFields),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.getISOFields),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.getISOFields.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/custom.js
new file mode 100644
index 0000000000..9af214f61e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/custom.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.getisofields
+description: getISOFields does not call into user code.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarThrowEverything();
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+const result = instance.getISOFields();
+
+assert.sameValue(result.isoYear, 2000, "isoYear result");
+assert.sameValue(result.isoMonth, 5, "isoMonth result");
+assert.sameValue(result.isoDay, 2, "isoDay result");
+assert.sameValue(result.calendar, calendar, "calendar result");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/field-names.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/field-names.js
new file mode 100644
index 0000000000..e9fea72883
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/field-names.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.getisofields
+description: Correct field names on the object returned from getISOFields
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+
+const result = date.getISOFields();
+assert.sameValue(result.isoYear, 2000, "isoYear result");
+assert.sameValue(result.isoMonth, 5, "isoMonth result");
+assert.sameValue(result.isoDay, 2, "isoDay result");
+assert.sameValue(result.calendar, "iso8601", "calendar result");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/field-prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/field-prop-desc.js
new file mode 100644
index 0000000000..3f033cb633
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/field-prop-desc.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.getisofields
+description: Properties on the returned object have the correct descriptor
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "calendar",
+ "isoDay",
+ "isoMonth",
+ "isoYear",
+];
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const result = date.getISOFields();
+
+for (const property of expected) {
+ verifyProperty(result, property, {
+ writable: true,
+ enumerable: true,
+ configurable: true,
+ });
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/field-traversal-order.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/field-traversal-order.js
new file mode 100644
index 0000000000..6417f1d3fd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/field-traversal-order.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.getisofields
+description: Properties added in correct order to object returned from getISOFields
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "calendar",
+ "isoDay",
+ "isoMonth",
+ "isoYear",
+];
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const result = date.getISOFields();
+
+assert.compareArray(Object.keys(result), expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/length.js
new file mode 100644
index 0000000000..34dd97f568
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.getisofields
+description: Temporal.PlainDate.prototype.getISOFields.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.getISOFields, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/name.js
new file mode 100644
index 0000000000..c4bb4b942c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.getisofields
+description: Temporal.PlainDate.prototype.getISOFields.name is "getISOFields".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.getISOFields, "name", {
+ value: "getISOFields",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/not-a-constructor.js
new file mode 100644
index 0000000000..3484530b71
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.getisofields
+description: >
+ Temporal.PlainDate.prototype.getISOFields does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.getISOFields();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.getISOFields), false,
+ "isConstructor(Temporal.PlainDate.prototype.getISOFields)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/prop-desc.js
new file mode 100644
index 0000000000..49af4b6386
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.getisofields
+description: The "getISOFields" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.getISOFields,
+ "function",
+ "`typeof PlainDate.prototype.getISOFields` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "getISOFields", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/prototype.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/prototype.js
new file mode 100644
index 0000000000..4dd3c9ebb6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/prototype.js
@@ -0,0 +1,15 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.getisofields
+description: Correct prototype on the object returned from getISOFields
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const result = instance.getISOFields();
+assert.sameValue(Object.getPrototypeOf(result), Object.prototype, "prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/basic.js
new file mode 100644
index 0000000000..8a7aabb4e8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/basic.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.inleapyear
+description: Basic test for inLeapYear
+features: [Temporal]
+---*/
+
+assert.sameValue((new Temporal.PlainDate(1976, 11, 18)).inLeapYear,
+ true, "leap year");
+assert.sameValue((new Temporal.PlainDate(1977, 11, 18)).inLeapYear,
+ false, "non-leap year");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/branding.js
new file mode 100644
index 0000000000..ef7e7fb073
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.inleapyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const inLeapYear = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "inLeapYear").get;
+
+assert.sameValue(typeof inLeapYear, "function");
+
+assert.throws(TypeError, () => inLeapYear.call(undefined), "undefined");
+assert.throws(TypeError, () => inLeapYear.call(null), "null");
+assert.throws(TypeError, () => inLeapYear.call(true), "true");
+assert.throws(TypeError, () => inLeapYear.call(""), "empty string");
+assert.throws(TypeError, () => inLeapYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => inLeapYear.call(1), "1");
+assert.throws(TypeError, () => inLeapYear.call({}), "plain object");
+assert.throws(TypeError, () => inLeapYear.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => inLeapYear.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..690016923a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.inleapyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const inLeapYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "inLeapYear");
+Object.defineProperty(Temporal.Calendar.prototype, "inLeapYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("inLeapYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.inLeapYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "inLeapYear", inLeapYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/custom.js
new file mode 100644
index 0000000000..5c7b285116
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.inleapyear
+description: Custom calendar tests for inLeapYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ inLeapYear(...args) {
+ ++calls;
+ assert.compareArray(args, [pd], "inLeapYear arguments");
+ return true;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pd = new Temporal.PlainDate(1830, 8, 25, calendar);
+const result = pd.inLeapYear;
+assert.sameValue(result, true, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/prop-desc.js
new file mode 100644
index 0000000000..9a1a9b4e48
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.inleapyear
+description: The "inLeapYear" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "inLeapYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/validate-calendar-value.js
new file mode 100644
index 0000000000..a21e641c24
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/validate-calendar-value.js
@@ -0,0 +1,56 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.inleapyear
+description: Validate result returned from calendar inLeapYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [0, TypeError],
+ [-0, TypeError],
+ [42, TypeError],
+ [7.1, TypeError],
+ [NaN, TypeError],
+ [Infinity, TypeError],
+ [-Infinity, TypeError],
+ ["", TypeError],
+ ["a string", TypeError],
+ ["0", TypeError],
+ [Symbol("foo"), TypeError],
+ [0n, TypeError],
+ [42n, TypeError],
+ [{}, TypeError],
+ [{valueOf() { return false; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ inLeapYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.throws(error, () => instance.inLeapYear, `${typeof result} ${String(result)} not converted to boolean`);
+});
+
+const preservedResults = [
+ true,
+ false,
+];
+
+preservedResults.forEach(result => {
+ const calendar = new class extends Temporal.Calendar {
+ inLeapYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.sameValue(instance.inLeapYear, result, `${typeof result} ${String(result)} preserved`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/branding.js
new file mode 100644
index 0000000000..c43783d1e9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.month
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const month = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "month").get;
+
+assert.sameValue(typeof month, "function");
+
+assert.throws(TypeError, () => month.call(undefined), "undefined");
+assert.throws(TypeError, () => month.call(null), "null");
+assert.throws(TypeError, () => month.call(true), "true");
+assert.throws(TypeError, () => month.call(""), "empty string");
+assert.throws(TypeError, () => month.call(Symbol()), "symbol");
+assert.throws(TypeError, () => month.call(1), "1");
+assert.throws(TypeError, () => month.call({}), "plain object");
+assert.throws(TypeError, () => month.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => month.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..267c9f16a7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.month
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const monthOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "month");
+Object.defineProperty(Temporal.Calendar.prototype, "month", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("month should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.month;
+
+Object.defineProperty(Temporal.Calendar.prototype, "month", monthOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/custom.js
new file mode 100644
index 0000000000..bec164e029
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.month
+description: Custom calendar tests for month().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ month(...args) {
+ ++calls;
+ assert.compareArray(args, [pd], "month arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pd = new Temporal.PlainDate(1830, 8, 25, calendar);
+const result = pd.month;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/prop-desc.js
new file mode 100644
index 0000000000..185197f175
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.month
+description: The "month" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "month");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/validate-calendar-value.js
new file mode 100644
index 0000000000..cf18755457
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/validate-calendar-value.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.month
+description: Validate result returned from calendar month() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ month() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.throws(error, () => instance.month, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/branding.js
new file mode 100644
index 0000000000..3c863bb2e7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.monthcode
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const monthCode = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "monthCode").get;
+
+assert.sameValue(typeof monthCode, "function");
+
+assert.throws(TypeError, () => monthCode.call(undefined), "undefined");
+assert.throws(TypeError, () => monthCode.call(null), "null");
+assert.throws(TypeError, () => monthCode.call(true), "true");
+assert.throws(TypeError, () => monthCode.call(""), "empty string");
+assert.throws(TypeError, () => monthCode.call(Symbol()), "symbol");
+assert.throws(TypeError, () => monthCode.call(1), "1");
+assert.throws(TypeError, () => monthCode.call({}), "plain object");
+assert.throws(TypeError, () => monthCode.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => monthCode.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..6d5f28b4e5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.monthcode
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const monthCodeOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "monthCode");
+Object.defineProperty(Temporal.Calendar.prototype, "monthCode", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("monthCode should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.monthCode;
+
+Object.defineProperty(Temporal.Calendar.prototype, "monthCode", monthCodeOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/custom.js
new file mode 100644
index 0000000000..922cb28b48
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.monthcode
+description: Custom calendar tests for monthCode().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ monthCode(...args) {
+ ++calls;
+ assert.compareArray(args, [pd], "monthCode arguments");
+ return "M01";
+ }
+}
+
+const calendar = new CustomCalendar();
+const pd = new Temporal.PlainDate(1830, 8, 25, calendar);
+const result = pd.monthCode;
+assert.sameValue(result, "M01", "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/prop-desc.js
new file mode 100644
index 0000000000..5200447989
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.monthcode
+description: The "monthCode" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "monthCode");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/validate-calendar-value.js
new file mode 100644
index 0000000000..0de050312b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/validate-calendar-value.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.monthcode
+description: Validate result returned from calendar monthCode() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [Symbol("foo"), TypeError],
+ [null, TypeError],
+ [true, TypeError],
+ [false, TypeError],
+ [7.1, TypeError],
+ [{toString() { return "M01"; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ monthCode() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.throws(error, () => instance.monthCode, `${typeof result} ${String(result)} not converted to string`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/basic.js
new file mode 100644
index 0000000000..e87dbbc43b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/basic.js
@@ -0,0 +1,14 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.monthsinyear
+description: Basic tests for monthsInYear().
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(1976, 11, 18);
+assert.sameValue(plainDate.monthsInYear, 12);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/branding.js
new file mode 100644
index 0000000000..02ce82dd31
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.monthsinyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const monthsInYear = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "monthsInYear").get;
+
+assert.sameValue(typeof monthsInYear, "function");
+
+assert.throws(TypeError, () => monthsInYear.call(undefined), "undefined");
+assert.throws(TypeError, () => monthsInYear.call(null), "null");
+assert.throws(TypeError, () => monthsInYear.call(true), "true");
+assert.throws(TypeError, () => monthsInYear.call(""), "empty string");
+assert.throws(TypeError, () => monthsInYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => monthsInYear.call(1), "1");
+assert.throws(TypeError, () => monthsInYear.call({}), "plain object");
+assert.throws(TypeError, () => monthsInYear.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => monthsInYear.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..10606a3ab6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.monthsinyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const monthsInYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "monthsInYear");
+Object.defineProperty(Temporal.Calendar.prototype, "monthsInYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("monthsInYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.monthsInYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "monthsInYear", monthsInYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/custom.js
new file mode 100644
index 0000000000..aa3d856629
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.monthsinyear
+description: Custom calendar tests for monthsInYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ monthsInYear(...args) {
+ ++calls;
+ assert.compareArray(args, [pd], "monthsInYear arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pd = new Temporal.PlainDate(1830, 8, 25, calendar);
+const result = pd.monthsInYear;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/prop-desc.js
new file mode 100644
index 0000000000..a93ed88867
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.monthsinyear
+description: The "monthsInYear" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "monthsInYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/validate-calendar-value.js
new file mode 100644
index 0000000000..d0cf9588bf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/validate-calendar-value.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.monthsinyear
+description: Validate result returned from calendar monthsInYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ monthsInYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.throws(error, () => instance.monthsInYear, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/prop-desc.js
new file mode 100644
index 0000000000..9f00e5b72c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/prop-desc.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-plaindate-prototype
+description: The "prototype" property of Temporal.PlainDate
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(typeof Temporal.PlainDate.prototype, "object");
+assert.notSameValue(Temporal.PlainDate.prototype, null);
+
+verifyProperty(Temporal.PlainDate, "prototype", {
+ writable: false,
+ enumerable: false,
+ configurable: false,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..39f7b6b3fc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.since(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..c9a2c2753b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-calendar-datefromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.since(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..08aa6aebf8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-constructor-in-calendar-fields.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+assert.throws(RangeError, () => instance.since(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..366ef14b08
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.PlainDate(2000, 5, 2);
+
+ assert.throws(RangeError, () => instance.since(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-leap-second.js
new file mode 100644
index 0000000000..37acb5ed55
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-leap-second.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Leap second is a valid ISO string for PlainDate
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2016, 12, 31);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.since(arg);
+TemporalHelpers.assertDuration(
+ result1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for PlainDate"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.since(arg);
+TemporalHelpers.assertDuration(
+ result2,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "second: 60 is ignored in property bag for PlainDate"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-number.js
new file mode 100644
index 0000000000..b7d52930c9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-number.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.since(arg),
+ 'Numbers cannot be used in place of an ISO string for PlainDate'
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-plaindatetime.js
new file mode 100644
index 0000000000..0524d02ca7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-plaindatetime.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.since
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainDate by reading internal slots
+info: |
+ sec-temporal.plaindate.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalDate(_other_).
+ sec-temporal-totemporaldate step 2.b:
+ b. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ i. Return ! CreateTemporalDate(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const result = date.since(datetime);
+ assert.sameValue(result.total({ unit: "nanoseconds" }), 0, "time part dropped");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..f625f82cff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: The calendar name is case-insensitive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.since(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..b028699f45
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Leap second is a valid ISO string for a calendar in a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.since(arg);
+TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..ef118b29ec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-number.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.since(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..78077d1cb5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-string.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.since(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..4f7bc89d08
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.since(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.since(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..13b4a25387
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-year-zero.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..21dd6f91da
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-proto-in-calendar-fields.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+assert.throws(RangeError, () => instance.since(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..c24c587a15
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-calendar-annotation.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.since(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..d3ff358725
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!foo=bar]",
+ "1970-01-01T00:00[!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..577856f88a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-date-with-utc-offset.js
@@ -0,0 +1,49 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.since(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+const invalidStrings = [
+ "2022-09-15Z",
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-invalid.js
new file mode 100644
index 0000000000..9767b0ef63
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-invalid.js
@@ -0,0 +1,64 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ `"${arg}" should not be a valid ISO string for a PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..5bb7388900
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-multiple-calendar.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..9f87b4dc02
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-multiple-time-zone.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[UTC][UTC]",
+ "1970-01-01T00:00[UTC][UTC]",
+ "1970-01-01T00:00[!UTC][UTC]",
+ "1970-01-01T00:00[UTC][!UTC]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00[UTC][foo=bar][UTC]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-time-separators.js
new file mode 100644
index 0000000000..f12c529db2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-time-separators.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.since(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..acf7c2972f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-time-zone-annotation.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[Asia/Kolkata]", "named, with no time"],
+ ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"],
+ ["2000-05-02[+00:00]", "numeric, with no time"],
+ ["2000-05-02[!-02:30]", "numeric, with ! and no time"],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.since(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..ba543410e2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-unknown-annotation.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.since(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..1d6700d894
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-with-utc-designator.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ "String with UTC designator should not be valid as a PlainDate"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-wrong-type.js
new file mode 100644
index 0000000000..8fcddedfba
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-wrong-type.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => instance.since(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.since(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-convert.js
new file mode 100644
index 0000000000..5f1ee07422
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+assert.throws(Test262Error, () => instance.since(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..1270b6d99f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-slots.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+const instance = new Temporal.PlainDate(1976, 11, 18);
+instance.since(arg);
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..8c06d06e06
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.since(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..2fd89d1bc2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => date.since(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..41e03c66bf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.since(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..46d51c9016
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => date.since(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/basic.js
new file mode 100644
index 0000000000..0f7e7221d4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/basic.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Basic tests for since().
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(1969, 7, 24);
+const plainDate2 = Temporal.PlainDate.from({ year: 1969, month: 10, day: 5 });
+TemporalHelpers.assertDuration(plainDate2.since(plainDate), 0, 0, 0, /* days = */ 73, 0, 0, 0, 0, 0, 0, "same year");
+
+const earlier = new Temporal.PlainDate(1969, 7, 24);
+const later = new Temporal.PlainDate(1996, 3, 3);
+const duration = later.since(earlier);
+TemporalHelpers.assertDuration(duration, 0, 0, 0, /* days = */ 9719, 0, 0, 0, 0, 0, 0, "different year");
+
+TemporalHelpers.assertDuration(plainDate.since({ year: 2019, month: 7, day: 24 }), 0, 0, 0, /* days = */ -18262, 0, 0, 0, 0, 0, 0, "option bag");
+TemporalHelpers.assertDuration(plainDate.since("2019-07-24"), 0, 0, 0, /* days = */ -18262, 0, 0, 0, 0, 0, 0, "string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/branding.js
new file mode 100644
index 0000000000..b6b33a0ed1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const since = Temporal.PlainDate.prototype.since;
+
+assert.sameValue(typeof since, "function");
+
+const args = [new Temporal.PlainDate(2022, 6, 22)];
+
+assert.throws(TypeError, () => since.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => since.apply(null, args), "null");
+assert.throws(TypeError, () => since.apply(true, args), "true");
+assert.throws(TypeError, () => since.apply("", args), "empty string");
+assert.throws(TypeError, () => since.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => since.apply(1, args), "1");
+assert.throws(TypeError, () => since.apply({}, args), "plain object");
+assert.throws(TypeError, () => since.apply(Temporal.PlainDate, args), "Temporal.PlainDate");
+assert.throws(TypeError, () => since.apply(Temporal.PlainDate.prototype, args), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..48abc72656
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateUntilOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateUntil");
+Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateUntil should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.since(new Temporal.PlainDate(1999, 4, 1));
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", dateUntilOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/builtin.js
new file mode 100644
index 0000000000..2a0e1056df
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: >
+ Tests that Temporal.PlainDate.prototype.since
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.since),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.since),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.since),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.since.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-dateadd-called-with-plaindate-instance.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-dateadd-called-with-plaindate-instance.js
new file mode 100644
index 0000000000..ccbfe65104
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-dateadd-called-with-plaindate-instance.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: >
+ relativeTo parameters that are not ZonedDateTime or undefined, are always
+ converted to PlainDate for observable calendar calls
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddPlainDateInstance();
+const instance = new Temporal.PlainDate(1970, 1, 1, calendar);
+calendar.specificPlainDate = instance;
+instance.since(new Temporal.PlainDate(2000, 5, 2, calendar), { smallestUnit: "month" });
+assert(calendar.dateAddCallCount > 0, "assertions in calendar.dateAdd() should have been tested");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..72eb3ada42
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-datefromfields-called-with-options-undefined.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+instance.since({ year: 2000, month: 5, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js
new file mode 100644
index 0000000000..68079f9d32
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: >
+ Calendar.dateUntil method is called with a null-prototype object as the
+ options value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckOptionsPrototypePollution();
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+const argument = new Temporal.PlainDate(2022, 6, 14, calendar);
+instance.since(argument, { largestUnit: "months" });
+assert.sameValue(calendar.dateUntilCallCount, 1, "dateUntil should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 0000000000..96382a3764
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.plaindate.prototype.since steps 13–14:
+ 13. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _largestUnit_).
+ 14. Let _result_ be ? CalendarDateUntil(_temporalDate_.[[Calendar]], _other_, _temporalDate_, _untilOptions_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const earlier = new Temporal.PlainDate(2000, 5, 2, calendar);
+ const later = new Temporal.PlainDate(2001, 6, 3, calendar);
+ later.since(earlier, { largestUnit });
+ },
+ {
+ years: ["year"],
+ months: ["month"],
+ weeks: ["week"],
+ days: []
+ }
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-fields-iterable.js
new file mode 100644
index 0000000000..73db3c85dd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-fields-iterable.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindate.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalDate(_other_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const date = new Temporal.PlainDate(2000, 5, 2, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+date.since({ year: 2005, month: 6, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-id-match.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-id-match.js
new file mode 100644
index 0000000000..9191a3635f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-id-match.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Calculation is performed if calendars' toString results match
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class Calendar1 extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ toString() {
+ return "A";
+ }
+}
+class Calendar2 extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ toString() {
+ return "A";
+ }
+}
+
+const plainDate1 = new Temporal.PlainDate(2000, 1, 1, new Calendar1());
+const plainDate2 = new Temporal.PlainDate(2000, 1, 2, new Calendar2());
+TemporalHelpers.assertDuration(plainDate2.since(plainDate1), 0, 0, 0, /* days = */ 1, 0, 0, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-invalid-return.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-invalid-return.js
new file mode 100644
index 0000000000..f008696b77
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-invalid-return.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Throw when the returned value from the calendar's dateUntil method is not a Duration.
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor(value) {
+ super("iso8601");
+ this.value = value;
+ }
+ dateUntil() {
+ return this.value;
+ }
+}
+
+const tests = [
+ [undefined],
+ [null, "null"],
+ [true],
+ ["2000-05"],
+ [Symbol()],
+ [200005],
+ [200005n],
+ [{}, "plain object"],
+ [() => {}, "lambda"],
+ [Temporal.Duration, "Temporal.Duration"],
+ [Temporal.Duration.prototype, "Temporal.Duration.prototype"],
+];
+for (const [test, description = typeof test] of tests) {
+ const plainDate = new Temporal.PlainDate(2000, 5, 2, new CustomCalendar(test));
+ assert.throws(
+ TypeError,
+ () => plainDate.since("2022-06-20", { largestUnit: "years" }),
+ `Expected error with ${description}`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-mismatch.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-mismatch.js
new file mode 100644
index 0000000000..1537dda6ee
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-mismatch.js
@@ -0,0 +1,62 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: RangeError thrown if calendars' id properties do not match
+features: [Temporal]
+---*/
+
+const calendar1 = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "A",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const calendar2 = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "B",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const plainDate1 = new Temporal.PlainDate(2000, 1, 1, calendar1);
+const plainDate2 = new Temporal.PlainDate(2000, 1, 1, calendar2);
+assert.throws(RangeError, () => plainDate1.since(plainDate2));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-temporal-object.js
new file mode 100644
index 0000000000..c99733f2d7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindate.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalDate(_other_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const date = new Temporal.PlainDate(2000, 5, 2, temporalObject);
+ date.since({ year: 2005, month: 6, day: 2, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/custom.js
new file mode 100644
index 0000000000..7aa8c4de2e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/custom.js
@@ -0,0 +1,35 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Basic tests with custom calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const result = new Temporal.Duration(1, 3, 5, 7, 9);
+const options = { largestUnit: "years" };
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateUntil(...args) {
+ ++calls;
+ assert.sameValue(args.length, 3, "Three arguments");
+ assert.sameValue(args[0], plainDate, "First argument");
+ assert.sameValue(args[1], other, "Second argument");
+ assert.sameValue(args[2].largestUnit, "year", "Third argument: largestUnit");
+ return result;
+ }
+}
+const calendar = new CustomCalendar();
+const plainDate = new Temporal.PlainDate(1976, 11, 18, calendar);
+const other = new Temporal.PlainDate(2022, 6, 20, calendar);
+TemporalHelpers.assertDuration(plainDate.since(other, options),
+ -1, -3, -5, -7, 0, 0, 0, 0, 0, 0, "result");
+assert.sameValue(calls, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/days-in-month.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/days-in-month.js
new file mode 100644
index 0000000000..4bce5ba5d5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/days-in-month.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: since() should take length of month into account.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate1 = Temporal.PlainDate.from("2019-01-01");
+const plainDate2 = Temporal.PlainDate.from("2019-02-01");
+const plainDate3 = Temporal.PlainDate.from("2019-03-01");
+TemporalHelpers.assertDuration(plainDate2.since(plainDate1), 0, 0, 0, /* days = */ 31, 0, 0, 0, 0, 0, 0, "January 2019");
+TemporalHelpers.assertDuration(plainDate3.since(plainDate2), 0, 0, 0, /* days = */ 28, 0, 0, 0, 0, 0, 0, "February 2019");
+
+const plainDate4 = Temporal.PlainDate.from("2020-02-01");
+const plainDate5 = Temporal.PlainDate.from("2020-03-01");
+TemporalHelpers.assertDuration(plainDate5.since(plainDate4), 0, 0, 0, /* days = */ 29, 0, 0, 0, 0, 0, 0, "February 2020");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/days-in-year.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/days-in-year.js
new file mode 100644
index 0000000000..852c3062f6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/days-in-year.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: since() should take length of year into account.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate1 = Temporal.PlainDate.from("2019-01-01");
+const plainDate2 = Temporal.PlainDate.from("2020-01-01");
+const plainDate3 = Temporal.PlainDate.from("2021-01-01");
+TemporalHelpers.assertDuration(plainDate2.since(plainDate1), 0, 0, 0, /* days = */ 365, 0, 0, 0, 0, 0, 0, "From January 2019");
+TemporalHelpers.assertDuration(plainDate3.since(plainDate2), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "From January 2020");
+
+const plainDate4 = Temporal.PlainDate.from("2019-06-01");
+const plainDate5 = Temporal.PlainDate.from("2020-06-01");
+const plainDate6 = Temporal.PlainDate.from("2021-06-01");
+TemporalHelpers.assertDuration(plainDate5.since(plainDate4), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "From June 2019");
+TemporalHelpers.assertDuration(plainDate6.since(plainDate5), 0, 0, 0, /* days = */ 365, 0, 0, 0, 0, 0, 0, "From June 2020");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..33d5c6c536
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/infinity-throws-rangeerror.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindate.prototype.since
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.since({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.since({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-default.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-default.js
new file mode 100644
index 0000000000..347254e0ab
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-default.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Default value for largestUnit option is days
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const feb20 = Temporal.PlainDate.from("2020-02-01");
+const feb21 = Temporal.PlainDate.from("2021-02-01");
+TemporalHelpers.assertDuration(feb21.since(feb20), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "no options");
+TemporalHelpers.assertDuration(feb21.since(feb20, undefined), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "undefined options");
+TemporalHelpers.assertDuration(feb21.since(feb20, {}), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "no largestUnit");
+TemporalHelpers.assertDuration(feb21.since(feb20, { largestUnit: undefined }), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "undefined largestUnit");
+TemporalHelpers.assertDuration(feb21.since(feb20, { largestUnit: "days" }), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "days");
+TemporalHelpers.assertDuration(feb21.since(feb20, { largestUnit: "auto" }), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "auto");
+TemporalHelpers.assertDuration(feb21.since(feb20, () => {}), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "no largestUnit (function)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-higher-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-higher-units.js
new file mode 100644
index 0000000000..89fbfe8c6f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-higher-units.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Tests calculations with higher largestUnit than the default of 'days'
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(1969, 7, 24);
+const later = Temporal.PlainDate.from({ year: 2019, month: 7, day: 24 });
+const duration = later.since(date, { largestUnit: "years" });
+TemporalHelpers.assertDuration(duration, /* years = */ 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, "crossing epoch");
+
+const feb20 = Temporal.PlainDate.from("2020-02-01");
+const feb21 = Temporal.PlainDate.from("2021-02-01");
+TemporalHelpers.assertDuration(feb21.since(feb20, { largestUnit: "years" }), /* years = */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "start of February, years");
+TemporalHelpers.assertDuration(feb21.since(feb20, { largestUnit: "months" }), 0, /* months = */ 12, 0, 0, 0, 0, 0, 0, 0, 0, "start of February, months");
+TemporalHelpers.assertDuration(feb21.since(feb20, { largestUnit: "weeks" }), 0, 0, /* weeks = */ 52, /* days = */ 2, 0, 0, 0, 0, 0, 0, "start of February, weeks");
+
+const lastFeb20 = Temporal.PlainDate.from("2020-02-29");
+const lastFeb21 = Temporal.PlainDate.from("2021-02-28");
+TemporalHelpers.assertDuration(lastFeb21.since(lastFeb20, { largestUnit: "years" }), 0, /* months = */ 11, 0, /* days = */ 28, 0, 0, 0, 0, 0, 0, "end of February, years");
+TemporalHelpers.assertDuration(lastFeb21.since(lastFeb20, { largestUnit: "months" }), 0, /* months = */ 11, 0, /* days = */ 28, 0, 0, 0, 0, 0, 0, "end of February, months");
+TemporalHelpers.assertDuration(lastFeb21.since(lastFeb20, { largestUnit: "weeks" }), 0, 0, /* weeks = */ 52, /* days = */ 1, 0, 0, 0, 0, 0, 0, "end of February, weeks");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-invalid-string.js
new file mode 100644
index 0000000000..f62f2effe5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-invalid-string.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+const badValues = [
+ "era",
+ "eraYear",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+ "month\0",
+ "YEAR",
+ "eras",
+ "eraYears",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+ "months\0",
+ "YEARS",
+ "other string"
+];
+for (const largestUnit of badValues) {
+ assert.throws(RangeError, () => later.since(earlier, { largestUnit }),
+ `"${largestUnit}" is not a valid value for largestUnit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-plurals-accepted.js
new file mode 100644
index 0000000000..cd93f33fd9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-plurals-accepted.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 12);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => later.since(earlier, { largestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-smallestunit-mismatch.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-smallestunit-mismatch.js
new file mode 100644
index 0000000000..f7ab10f829
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-smallestunit-mismatch.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: RangeError thrown when smallestUnit is larger than largestUnit
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+const units = ["years", "months", "weeks", "days"];
+for (let largestIdx = 1; largestIdx < units.length; largestIdx++) {
+ for (let smallestIdx = 0; smallestIdx < largestIdx; smallestIdx++) {
+ const largestUnit = units[largestIdx];
+ const smallestUnit = units[smallestIdx];
+ assert.throws(RangeError, () => later.since(earlier, { largestUnit, smallestUnit }));
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-undefined.js
new file mode 100644
index 0000000000..f12b94d2d9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-undefined.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Fallback value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+
+const explicit = later.since(earlier, { largestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 397, 0, 0, 0, 0, 0, 0, "default largestUnit is day");
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 397, 0, 0, 0, 0, 0, 0, "default largestUnit is day");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-wrong-type.js
new file mode 100644
index 0000000000..6fc693637d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-wrong-type.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "year",
+ (largestUnit) => later.since(earlier, { largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit.js
new file mode 100644
index 0000000000..1f78521659
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Specify behavior of PlainDate.since when largest specified unit is years or months.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+const pd1 = new Temporal.PlainDate(2020, 11, 1);
+const pd2 = new Temporal.PlainDate(2021, 11, 30);
+TemporalHelpers.assertDuration(pd2.since(pd1), 0, 0, 0, 394, 0, 0, 0, 0, 0, 0, 'does not include higher units than necessary (largest unit unspecified)');
+TemporalHelpers.assertDuration(pd2.since(pd1, { largestUnit: 'months' }), 0, 12, 0, 29, 0, 0, 0, 0, 0, 0, 'does not include higher units than necessary (largest unit is months)');
+TemporalHelpers.assertDuration(pd2.since(pd1, { largestUnit: 'years' }), 1, 0, 0, 29, 0, 0, 0, 0, 0, 0, 'does not include higher units than necessary (largest unit is years)');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/length.js
new file mode 100644
index 0000000000..32fd4e2a83
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Temporal.PlainDate.prototype.since.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.since, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/name.js
new file mode 100644
index 0000000000..70e86fb9e9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Temporal.PlainDate.prototype.since.name is "since".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.since, "name", {
+ value: "since",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/not-a-constructor.js
new file mode 100644
index 0000000000..addff2bce7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: >
+ Temporal.PlainDate.prototype.since does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.since();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.since), false,
+ "isConstructor(Temporal.PlainDate.prototype.since)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/options-object.js
new file mode 100644
index 0000000000..e3cfe462a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/options-object.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const result1 = instance.since(new Temporal.PlainDate(1976, 11, 18), {});
+TemporalHelpers.assertDuration(
+ result1, 0, 0, 0, 8566, 0, 0, 0, 0, 0, 0,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.since(new Temporal.PlainDate(1976, 11, 18), () => {});
+TemporalHelpers.assertDuration(
+ result2, 0, 0, 0, 8566, 0, 0, 0, 0, 0, 0,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/options-wrong-type.js
new file mode 100644
index 0000000000..6c95b9436c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/options-wrong-type.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.since(new Temporal.PlainDate(1976, 11, 18), value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/order-of-operations.js
new file mode 100644
index 0000000000..1214a1cd49
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/order-of-operations.js
@@ -0,0 +1,213 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Properties on objects passed to since() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalDate
+ "get other.calendar",
+ "has other.calendar.dateAdd",
+ "has other.calendar.dateFromFields",
+ "has other.calendar.dateUntil",
+ "has other.calendar.day",
+ "has other.calendar.dayOfWeek",
+ "has other.calendar.dayOfYear",
+ "has other.calendar.daysInMonth",
+ "has other.calendar.daysInWeek",
+ "has other.calendar.daysInYear",
+ "has other.calendar.fields",
+ "has other.calendar.id",
+ "has other.calendar.inLeapYear",
+ "has other.calendar.mergeFields",
+ "has other.calendar.month",
+ "has other.calendar.monthCode",
+ "has other.calendar.monthDayFromFields",
+ "has other.calendar.monthsInYear",
+ "has other.calendar.weekOfYear",
+ "has other.calendar.year",
+ "has other.calendar.yearMonthFromFields",
+ "has other.calendar.yearOfWeek",
+ "get other.calendar.dateFromFields",
+ "get other.calendar.fields",
+ "call other.calendar.fields",
+ "get other.day",
+ "get other.day.valueOf",
+ "call other.day.valueOf",
+ "get other.month",
+ "get other.month.valueOf",
+ "call other.month.valueOf",
+ "get other.monthCode",
+ "get other.monthCode.toString",
+ "call other.monthCode.toString",
+ "get other.year",
+ "get other.year.valueOf",
+ "call other.year.valueOf",
+ "call other.calendar.dateFromFields",
+ // CalendarEquals
+ "get this.calendar.id",
+ "get other.calendar.id",
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.roundingIncrement",
+ "get options.roundingIncrement",
+ "getOwnPropertyDescriptor options.roundingMode",
+ "get options.roundingMode",
+ "getOwnPropertyDescriptor options.largestUnit",
+ "get options.largestUnit",
+ "getOwnPropertyDescriptor options.smallestUnit",
+ "get options.smallestUnit",
+ "getOwnPropertyDescriptor options.additional",
+ "get options.additional",
+ // GetDifferenceSettings
+ "get options.largestUnit.toString",
+ "call options.largestUnit.toString",
+ "get options.roundingIncrement.valueOf",
+ "call options.roundingIncrement.valueOf",
+ "get options.roundingMode.toString",
+ "call options.roundingMode.toString",
+ "get options.smallestUnit.toString",
+ "call options.smallestUnit.toString",
+];
+const actual = [];
+
+const ownCalendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainDate(2000, 5, 2, ownCalendar);
+
+const otherDatePropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 6,
+ monthCode: "M06",
+ day: 2,
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+
+function createOptionsObserver({ smallestUnit = "days", largestUnit = "auto", roundingMode = "halfExpand", roundingIncrement = 1 } = {}) {
+ return TemporalHelpers.propertyBagObserver(actual, {
+ // order is significant, due to iterating through properties in order to
+ // copy them to an internal null-prototype object:
+ roundingIncrement,
+ roundingMode,
+ largestUnit,
+ smallestUnit,
+ additional: "property",
+ }, "options");
+}
+
+// clear any observable things that happened while constructing the objects
+actual.splice(0);
+
+// basic order of observable operations with calendar call, without rounding:
+instance.since(otherDatePropertyBag, createOptionsObserver({ largestUnit: "years" }));
+assert.compareArray(actual, expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+]), "order of operations");
+actual.splice(0); // clear
+
+// short-circuit for identical objects:
+const identicalPropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 5,
+ monthCode: "M05",
+ day: 2,
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+
+instance.since(identicalPropertyBag, createOptionsObserver());
+assert.compareArray(actual, expected, "order of operations with identical dates");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year:
+const expectedOpsForYearRounding = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateAdd", // 12.d
+ "call this.calendar.dateAdd", // 12.f
+ "call this.calendar.dateUntil", // 12.n
+ "call this.calendar.dateAdd", // 12.x MoveRelativeDate
+ // (12.r not called because other units can't add up to >1 year at this point)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 9.c
+ "call this.calendar.dateUntil" // 9.d
+]);
+instance.since(otherDatePropertyBag, createOptionsObserver({ smallestUnit: "years" }));
+assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year and skips a DateUntil call:
+const otherDatePropertyBagSameMonth = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 5,
+ monthCode: "M05",
+ day: 2,
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+const expectedOpsForYearRoundingSameMonth = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateAdd", // 12.d
+ "call this.calendar.dateAdd", // 12.f
+ "call this.calendar.dateAdd", // 12.x MoveRelativeDate
+ // (12.n not called because months and weeks == 0)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 9.c
+ "call this.calendar.dateUntil" // 9.d
+]);
+instance.since(otherDatePropertyBagSameMonth, createOptionsObserver({ smallestUnit: "years" }));
+assert.compareArray(actual, expectedOpsForYearRoundingSameMonth, "order of operations with smallestUnit = years and no excess months/weeks");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest month:
+const expectedOpsForMonthRounding = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateAdd", // 13.c
+ "call this.calendar.dateAdd", // 13.e
+ "call this.calendar.dateAdd", // 13.w MoveRelativeDate
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 10.d
+ "call this.calendar.dateUntil" // 10.e
+]);
+instance.since(otherDatePropertyBag, createOptionsObserver({ smallestUnit: "months" }));
+assert.compareArray(actual, expectedOpsForMonthRounding, "order of operations with smallestUnit = months");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest week:
+const expectedOpsForWeekRounding = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateUntil", // 14.f
+ "call this.calendar.dateAdd", // 14.p MoveRelativeDate
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 16
+ "call this.calendar.dateUntil" // 17
+]);
+instance.since(otherDatePropertyBag, createOptionsObserver({ smallestUnit: "weeks" }));
+assert.compareArray(actual, expectedOpsForWeekRounding, "order of operations with smallestUnit = weeks");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/prop-desc.js
new file mode 100644
index 0000000000..cbebd49dad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: The "since" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.since,
+ "function",
+ "`typeof PlainDate.prototype.since` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "since", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/round-cross-unit-boundary.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/round-cross-unit-boundary.js
new file mode 100644
index 0000000000..48f8b822c4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/round-cross-unit-boundary.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Rounding can cross unit boundaries up to largestUnit
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2022, 1, 1);
+const later = new Temporal.PlainDate(2023, 12, 25);
+const duration = earlier.since(later, { largestUnit: "years", smallestUnit: "months", roundingMode: "expand" });
+TemporalHelpers.assertDuration(duration, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "-1 year -11 months balances to -2 years");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/rounding-relative.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/rounding-relative.js
new file mode 100644
index 0000000000..fe26ed0e44
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/rounding-relative.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Should round relative to the receiver.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date1 = Temporal.PlainDate.from("2019-01-01");
+const date2 = Temporal.PlainDate.from("2019-02-15");
+
+TemporalHelpers.assertDuration(
+ date2.since(date1, { smallestUnit: "months", roundingMode: "halfExpand" }),
+ 0, /* months = */ 1, 0, 0, 0, 0, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(
+ date1.since(date2, { smallestUnit: "months", roundingMode: "halfExpand" }),
+ 0, /* months = */ -2, 0, 0, 0, 0, 0, 0, 0, 0);
+
+const cases = [
+ ["2019-03-01", "2019-01-29", 1, 3],
+ ["2019-01-29", "2019-03-01", -1, -1],
+ ["2019-03-29", "2019-01-30", 1, 29],
+ ["2019-01-30", "2019-03-29", -1, -29],
+ ["2019-03-30", "2019-01-31", 1, 28],
+ ["2019-01-31", "2019-03-30", -1, -30],
+ ["2019-03-31", "2019-01-31", 2, 0],
+ ["2019-01-31", "2019-03-31", -2, 0]
+];
+for (const [end, start, months, days] of cases) {
+ const result = Temporal.PlainDate.from(end).since(start, { largestUnit: "months" });
+ TemporalHelpers.assertDuration(result, 0, months, 0, days, 0, 0, 0, 0, 0, 0, `${end} - ${start}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/rounding-zero-year-month-week-length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/rounding-zero-year-month-week-length.js
new file mode 100644
index 0000000000..0815cd30a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/rounding-zero-year-month-week-length.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: >
+ A malicious calendar resulting in a year, month, or week length of zero is
+ handled correctly
+info: |
+ RoundDuration
+ 10.z. If _oneYearDays_ = 0, throw a *RangeError* exception.
+ ...
+ 11.z. If _oneMonthDays_ = 0, throw a *RangeError* exception.
+ ...
+ 12.s. If _oneWeekDays_ = 0, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const cal = new class extends Temporal.Calendar {
+ dateAdd(date, duration, options) {
+ // Called several times, last call sets oneYear/Month/WeekDays to 0
+ return new Temporal.PlainDate(1970, 1, 1);
+ }
+}("iso8601");
+
+const d1 = new Temporal.PlainDate(1970, 1, 1, cal);
+const d2 = new Temporal.PlainDate(1971, 1, 1, cal);
+
+assert.throws(RangeError, () => d1.since(d2, { smallestUnit: "years" }), "zero year length handled correctly");
+assert.throws(RangeError, () => d1.since(d2, { smallestUnit: "months" }), "zero month length handled correctly");
+assert.throws(RangeError, () => d1.since(d2, { smallestUnit: "weeks" }), "zero week length handled correctly");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-nan.js
new file mode 100644
index 0000000000..147002ea9d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-nan.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: RangeError thrown when roundingIncrement option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaindate.prototype.since step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-non-integer.js
new file mode 100644
index 0000000000..350d8aea2e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-non-integer.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Rounding for roundingIncrement option
+info: |
+ ToTemporalRoundingIncrement ( _normalizedOptions_ )
+
+ 1. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, *"number"*, *undefined*, *1*<sub>𝔽</sub>).
+ 2. If _increment_ is not finite, throw a *RangeError* exception.
+ 3. Let _integerIncrement_ be truncate(ℝ(_increment_)).
+ 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception.
+ 5. Return _integerIncrement_.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+const result = later.since(earlier, { roundingIncrement: 2.5, roundingMode: "trunc" });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, "roundingIncrement 2.5 truncates to 2");
+const result2 = later.since(earlier, { smallestUnit: "days", roundingIncrement: 1e9 + 0.5, roundingMode: "expand" });
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 1e9, 0, 0, 0, 0, 0, 0, "roundingIncrement 1e9 + 0.5 truncates to 1e9");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-out-of-range.js
new file mode 100644
index 0000000000..b08717d226
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-out-of-range.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: RangeError thrown when roundingIncrement option out of range
+info: |
+ ToTemporalRoundingIncrement ( _normalizedOptions_ )
+
+ 1. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, *"number"*, *undefined*, *1*<sub>𝔽</sub>).
+ 2. If _increment_ is not finite, throw a *RangeError* exception.
+ 3. Let _integerIncrement_ be truncate(ℝ(_increment_)).
+ 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception.
+ 5. Return _integerIncrement_.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: -1 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: 0 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: 0.9 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: 1e9 + 1 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: Infinity }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-undefined.js
new file mode 100644
index 0000000000..929967235b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-undefined.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Fallback value for roundingIncrement option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaindate.prototype.since step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+
+const explicit = later.since(earlier, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, "default roundingIncrement is 1");
+
+// See options-undefined.js for {}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-wrong-type.js
new file mode 100644
index 0000000000..c7ed2fee9d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-wrong-type.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Type conversions for roundingIncrement option
+info: |
+ sec-getoption step 8.a:
+ a. Set _value_ to ? ToNumber(value).
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaindate.prototype.since step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => later.since(earlier, { roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement.js
new file mode 100644
index 0000000000..5873e3b25f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Tests calculations with roundingMode "trunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = Temporal.PlainDate.from("2019-01-08");
+const later = Temporal.PlainDate.from("2021-09-07");
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "years", roundingIncrement: 4, roundingMode: "halfExpand" }),
+ /* years = */ 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, "years");
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "months", roundingIncrement: 10, roundingMode: "halfExpand" }),
+ 0, /* months = */ 30, 0, 0, 0, 0, 0, 0, 0, 0, "months");
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "weeks", roundingIncrement: 12, roundingMode: "halfExpand" }),
+ 0, 0, /* weeks = */ 144, 0, 0, 0, 0, 0, 0, 0, "weeks");
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "days", roundingIncrement: 100, roundingMode: "halfExpand" }),
+ 0, 0, 0, /* days = */ 1000, 0, 0, 0, 0, 0, 0, "days");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-ceil.js
new file mode 100644
index 0000000000..8a64c7afe3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-ceil.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Tests calculations with roundingMode "ceil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [3], [-2]],
+ ["months", [0, 32], [0, -31]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+const roundingMode = "ceil";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-expand.js
new file mode 100644
index 0000000000..a869c012ad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-expand.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Tests calculations with roundingMode "expand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+const roundingMode = "expand";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-floor.js
new file mode 100644
index 0000000000..6048242bbf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-floor.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Tests calculations with roundingMode "floor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [2], [-3]],
+ ["months", [0, 31], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+const roundingMode = "floor";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..7bdf5db27f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfCeil.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Tests calculations with roundingMode "halfCeil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+const roundingMode = "halfCeil";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfEven.js
new file mode 100644
index 0000000000..f95cb286eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfEven.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Tests calculations with roundingMode "halfEven".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+const roundingMode = "halfEven";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..1b137fa714
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfExpand.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Tests calculations with roundingMode "halfExpand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+const roundingMode = "halfExpand";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..fd2112d032
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfFloor.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Tests calculations with roundingMode "halfFloor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+const roundingMode = "halfFloor";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..e7e36d28f0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfTrunc.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Tests calculations with roundingMode "halfTrunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+const roundingMode = "halfTrunc";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..fe4245ee3b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-invalid-string.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) {
+ assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "microsecond", roundingMode }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-trunc.js
new file mode 100644
index 0000000000..6b2deedab4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-trunc.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Tests calculations with roundingMode "trunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [2], [-2]],
+ ["months", [0, 31], [0, -31]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+const roundingMode = "trunc";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-undefined.js
new file mode 100644
index 0000000000..d0fe47a652
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-undefined.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 1, 1);
+
+const later1 = new Temporal.PlainDate(2005, 2, 20);
+const explicit1 = later1.since(earlier, { smallestUnit: "year", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+const implicit1 = later1.since(earlier, { smallestUnit: "year" });
+TemporalHelpers.assertDuration(implicit1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+
+const later2 = new Temporal.PlainDate(2005, 12, 15);
+const explicit2 = later2.since(earlier, { smallestUnit: "year", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+const implicit2 = later2.since(earlier, { smallestUnit: "year" });
+TemporalHelpers.assertDuration(implicit2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..9f69923d72
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-wrong-type.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => later.since(earlier, { smallestUnit: "year", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-higher-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-higher-units.js
new file mode 100644
index 0000000000..906fa73ecc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-higher-units.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Tests calculations with higher smallestUnit than the default of "days"
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = Temporal.PlainDate.from("2019-01-08");
+const later = Temporal.PlainDate.from("2021-09-07");
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "years", roundingMode: "halfExpand" }),
+ /* years = */ 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, "years");
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "months", roundingMode: "halfExpand" }),
+ 0, /* months = */ 32, 0, 0, 0, 0, 0, 0, 0, 0, "months");
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "weeks", roundingMode: "halfExpand" }),
+ 0, 0, /* weeks = */ 139, 0, 0, 0, 0, 0, 0, 0, "weeks");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..40b84325d3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-invalid-string.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+const badValues = [
+ "era",
+ "eraYear",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+ "month\0",
+ "YEAR",
+ "eras",
+ "eraYears",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+ "months\0",
+ "YEARS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => later.since(earlier, { smallestUnit }),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..e60ddefa73
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-plurals-accepted.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 12);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => later.since(earlier, { smallestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-undefined.js
new file mode 100644
index 0000000000..a27bf87ec4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-undefined.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+
+const explicit = later.since(earlier, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 397, 0, 0, 0, 0, 0, 0, "default smallestUnit is day");
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 397, 0, 0, 0, 0, 0, 0, "default smallestUnit is day");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..c55f5caf9a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-wrong-type.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "year",
+ (smallestUnit) => later.since(earlier, { smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/weeks-months.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/weeks-months.js
new file mode 100644
index 0000000000..d22b6914ea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/weeks-months.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: since() should not return weeks and months together.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(1969, 7, 24);
+const laterDate = new Temporal.PlainDate(1969, 9, 4);
+TemporalHelpers.assertDuration(laterDate.since(date, { largestUnit: "weeks" }),
+ 0, 0, /* weeks = */ 6, 0, 0, 0, 0, 0, 0, 0, "weeks");
+TemporalHelpers.assertDuration(laterDate.since(date, { largestUnit: "months" }),
+ 0, /* months = */ 1, 0, 11, 0, 0, 0, 0, 0, 0, "months");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/year-zero.js
new file mode 100644
index 0000000000..d894c5036c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/year-zero.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-duration-max.js
new file mode 100644
index 0000000000..09d307861f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-duration-max.js
@@ -0,0 +1,58 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Maximum allowed duration
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1970, 1, 1);
+
+const maxCases = [
+ ["P273790Y8M12DT23H59M59.999999999S", "string with max years"],
+ [{ years: 273790, months: 8, days: 12, nanoseconds: 86399999999999 }, "property bag with max years"],
+ ["P3285488M12DT23H59M59.999999999S", "string with max months"],
+ [{ months: 3285488, days: 12, nanoseconds: 86399999999999 }, "property bag with max months"],
+ ["P14285714W3DT23H59M59.999999999S", "string with max weeks"],
+ [{ weeks: 14285714, days: 3, nanoseconds: 86399999999999 }, "property bag with max weeks"],
+ ["P100000001DT23H59M59.999999999S", "string with max days"],
+ [{ days: 100000001, nanoseconds: 86399999999999 }, "property bag with max days"],
+ ["PT2400000047H59M59.999999999S", "string with max hours"],
+ [{ hours: 2400000047, nanoseconds: 3599999999999 }, "property bag with max hours"],
+ ["PT144000002879M59.999999999S", "string with max minutes"],
+ [{ minutes: 144000002879, nanoseconds: 59999999999 }, "property bag with max minutes"],
+ ["PT8640000172799.999999999S", "string with max seconds"],
+ [{ seconds: 8640000172799, nanoseconds: 999999999 }, "property bag with max seconds"],
+];
+
+for (const [arg, descr] of maxCases) {
+ const result = instance.subtract(arg);
+ TemporalHelpers.assertPlainDate(result, -271821, 4, "M04", 19, `operation succeeds with ${descr}`);
+}
+
+const minCases = [
+ ["-P273790Y8M12DT23H59M59.999999999S", "string with min years"],
+ [{ years: -273790, months: -8, days: -12, nanoseconds: -86399999999999 }, "property bag with min years"],
+ ["-P3285488M12DT23H59M59.999999999S", "string with min months"],
+ [{ months: -3285488, days: -12, nanoseconds: -86399999999999 }, "property bag with min months"],
+ ["-P14285714W2DT23H59M59.999999999S", "string with min weeks"],
+ [{ weeks: -14285714, days: -2, nanoseconds: -86399999999999 }, "property bag with min weeks"],
+ ["-P100000000DT23H59M59.999999999S", "string with min days"],
+ [{ days: -100000000, nanoseconds: -86399999999999 }, "property bag with min days"],
+ ["-PT2400000023H59M59.999999999S", "string with min hours"],
+ [{ hours: -2400000023, nanoseconds: -3599999999999 }, "property bag with min hours"],
+ ["-PT144000001439M59.999999999S", "string with min minutes"],
+ [{ minutes: -144000001439, nanoseconds: -59999999999 }, "property bag with min minutes"],
+ ["-PT8640000086399.999999999S", "string with min seconds"],
+ [{ seconds: -8640000086399, nanoseconds: -999999999 }, "property bag with min seconds"],
+];
+
+for (const [arg, descr] of minCases) {
+ const result = instance.subtract(arg);
+ TemporalHelpers.assertPlainDate(result, 275760, 9, "M09", 13, `operation succeeds with ${descr}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-duration-out-of-range.js
new file mode 100644
index 0000000000..5dbcc8de3c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-duration-out-of-range.js
@@ -0,0 +1,75 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Duration-like argument that is out of range
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1970, 1, 1);
+
+const cases = [
+ // 2^32 = 4294967296
+ ["P4294967296Y", "string with years > max"],
+ [{ years: 4294967296 }, "property bag with years > max"],
+ ["-P4294967296Y", "string with years < min"],
+ [{ years: -4294967296 }, "property bag with years < min"],
+ ["P4294967296M", "string with months > max"],
+ [{ months: 4294967296 }, "property bag with months > max"],
+ ["-P4294967296M", "string with months < min"],
+ [{ months: -4294967296 }, "property bag with months < min"],
+ ["P4294967296W", "string with weeks > max"],
+ [{ weeks: 4294967296 }, "property bag with weeks > max"],
+ ["-P4294967296W", "string with weeks < min"],
+ [{ weeks: -4294967296 }, "property bag with weeks < min"],
+
+ // ceil(max safe integer / 86400) = 104249991375
+ ["P104249991375D", "string with days > max"],
+ [{ days: 104249991375 }, "property bag with days > max"],
+ ["P104249991374DT24H", "string where hours balance into days > max"],
+ [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"],
+ ["-P104249991375D", "string with days < min"],
+ [{ days: -104249991375 }, "property bag with days < min"],
+ ["-P104249991374DT24H", "string where hours balance into days < min"],
+ [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"],
+
+ // ceil(max safe integer / 3600) = 2501999792984
+ ["PT2501999792984H", "string with hours > max"],
+ [{ hours: 2501999792984 }, "property bag with hours > max"],
+ ["PT2501999792983H60M", "string where minutes balance into hours > max"],
+ [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"],
+ ["-PT2501999792984H", "string with hours < min"],
+ [{ hours: -2501999792984 }, "property bag with hours < min"],
+ ["-PT2501999792983H60M", "string where minutes balance into hours < min"],
+ [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"],
+
+ // ceil(max safe integer / 60) = 150119987579017
+ ["PT150119987579017M", "string with minutes > max"],
+ [{ minutes: 150119987579017 }, "property bag with minutes > max"],
+ ["PT150119987579016M60S", "string where seconds balance into minutes > max"],
+ [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"],
+ ["-PT150119987579017M", "string with minutes < min"],
+ [{ minutes: -150119987579017 }, "property bag with minutes < min"],
+ ["-PT150119987579016M60S", "string where seconds balance into minutes < min"],
+ [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"],
+
+ // 2^53 = 9007199254740992
+ ["PT9007199254740992S", "string with seconds > max"],
+ [{ seconds: 9007199254740992 }, "property bag with seconds > max"],
+ [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"],
+ ["-PT9007199254740992S", "string with seconds < min"],
+ [{ seconds: -9007199254740992 }, "property bag with seconds < min"],
+ [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"],
+];
+
+for (const [arg, descr] of cases) {
+ assert.throws(RangeError, () => instance.subtract(arg), `${descr} is out of range`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-invalid-property.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-invalid-property.js
new file mode 100644
index 0000000000..85deb4d369
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-invalid-property.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: temporalDurationLike object must contain at least one correctly spelled property
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({}),
+ "Throws TypeError if no property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({ nonsense: true }),
+ "Throws TypeError if no recognized property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({ sign: 1 }),
+ "Sign property is not recognized"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-mixed-sign.js
new file mode 100644
index 0000000000..82ce5272a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-mixed-sign.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Positive and negative values in the temporalDurationLike argument are not acceptable
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+["constrain", "reject"].forEach((overflow) => {
+ assert.throws(
+ RangeError,
+ () => instance.subtract({ hours: 1, minutes: -30 }, { overflow }),
+ `mixed positive and negative values always throw (overflow = "${overflow}")`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-not-object.js
new file mode 100644
index 0000000000..7bf90c463d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-not-object.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Passing a primitive other than string to subtract() throws
+features: [Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+assert.throws(TypeError, () => instance.subtract(undefined), "undefined");
+assert.throws(TypeError, () => instance.subtract(null), "null");
+assert.throws(TypeError, () => instance.subtract(true), "boolean");
+assert.throws(RangeError, () => instance.subtract(""), "empty string");
+assert.throws(TypeError, () => instance.subtract(Symbol()), "Symbol");
+assert.throws(TypeError, () => instance.subtract(7), "number");
+assert.throws(TypeError, () => instance.subtract(7n), "bigint");
+assert.throws(TypeError, () => instance.subtract([]), "array");
+assert.throws(TypeError, () => instance.subtract(() => {}), "function");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-singular-properties.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-singular-properties.js
new file mode 100644
index 0000000000..a9c76a26de
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-singular-properties.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Singular properties in the property bag are always ignored
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+[
+ { year: 1 },
+ { month: 2 },
+ { week: 3 },
+ { day: 4 },
+ { hour: 5 },
+ { minute: 6 },
+ { second: 7 },
+ { millisecond: 8 },
+ { microsecond: 9 },
+ { nanosecond: 10 },
+].forEach((badObject) => {
+ assert.throws(TypeError, () => instance.subtract(badObject),
+ "Throw TypeError if temporalDurationLike is not valid");
+});
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-string-negative-fractional-units.js
new file mode 100644
index 0000000000..21fe56e530
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-string-negative-fractional-units.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const resultHours = instance.subtract("-PT24.567890123H");
+TemporalHelpers.assertPlainDate(resultHours, 2000, 5, "M05", 3, "negative fractional hours");
+
+const resultMinutes = instance.subtract("-PT1440.567890123M");
+TemporalHelpers.assertPlainDate(resultMinutes, 2000, 5, "M05", 3, "negative fractional minutes");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-string.js
new file mode 100644
index 0000000000..168af12c46
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-string.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: A string is parsed into the correct object when passed as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = Temporal.PlainDate.from({ year: 2000, month: 5, day: 2 });
+const result = instance.subtract("P3D");
+TemporalHelpers.assertPlainDate(result, 2000, 4, "M04", 29);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/balance-smaller-units-basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/balance-smaller-units-basic.js
new file mode 100644
index 0000000000..9c3556b488
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/balance-smaller-units-basic.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: Durations with units smaller than days are balanced
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(1976, 11, 18);
+
+// lower units that don't balance up to a day
+TemporalHelpers.assertPlainDate(date.subtract({ hours: 1 }), 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(date.subtract({ minutes: 1 }), 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(date.subtract({ seconds: 1 }), 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(date.subtract({ milliseconds: 1 }), 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(date.subtract({ microseconds: 1 }), 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(date.subtract({ nanoseconds: 1 }), 1976, 11, "M11", 18);
+
+// lower units that balance up to a day or more
+TemporalHelpers.assertPlainDate(date.subtract({ hours: 24 }), 1976, 11, "M11", 17);
+TemporalHelpers.assertPlainDate(date.subtract({ hours: 36 }), 1976, 11, "M11", 17);
+TemporalHelpers.assertPlainDate(date.subtract({ hours: 48 }), 1976, 11, "M11", 16);
+TemporalHelpers.assertPlainDate(date.subtract({ minutes: 1440 }), 1976, 11, "M11", 17);
+TemporalHelpers.assertPlainDate(date.subtract({ seconds: 86400 }), 1976, 11, "M11", 17);
+TemporalHelpers.assertPlainDate(date.subtract({ milliseconds: 86400_000 }), 1976, 11, "M11", 17);
+TemporalHelpers.assertPlainDate(date.subtract({ microseconds: 86400_000_000 }), 1976, 11, "M11", 17);
+TemporalHelpers.assertPlainDate(date.subtract({ nanoseconds: 86400_000_000_000 }), 1976, 11, "M11", 17);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/balance-smaller-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/balance-smaller-units.js
new file mode 100644
index 0000000000..5c04b17496
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/balance-smaller-units.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: Durations with units smaller than days are balanced before adding, in the calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const duration = new Temporal.Duration(0, 0, 0, 1, 24, 1440, 86400, 86400_000, 86400_000_000, 86400_000_000_000);
+
+const result = date.subtract(duration);
+TemporalHelpers.assertPlainDate(result, 2000, 4, "M04", 25, "units smaller than days are balanced");
+
+const resultString = date.subtract("P1DT24H1440M86400S");
+TemporalHelpers.assertPlainDate(resultString, 2000, 4, "M04", 28, "units smaller than days are balanced");
+
+const resultPropBag = date.subtract({ days: 1, hours: 24, minutes: 1440, seconds: 86400, milliseconds: 86400_000, microseconds: 86400_000_000, nanoseconds: 86400_000_000_000 });
+TemporalHelpers.assertPlainDate(resultPropBag, 2000, 4, "M04", 25, "units smaller than days are balanced");
+
+const negativeDuration = new Temporal.Duration(0, 0, 0, -1, -24, -1440, -86400, -86400_000, -86400_000_000, -86400_000_000_000);
+const resultNegative = date.subtract(negativeDuration);
+TemporalHelpers.assertPlainDate(resultNegative, 2000, 5, "M05", 9, "units smaller than days are balanced");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/basic.js
new file mode 100644
index 0000000000..a7a928366b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/basic.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Basic tests
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = Temporal.PlainDate.from("2019-11-18");
+TemporalHelpers.assertPlainDate(date.subtract({ years: 43 }),
+ 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(date.subtract({ months: 11 }),
+ 2018, 12, "M12", 18);
+TemporalHelpers.assertPlainDate(date.subtract({ days: 20 }),
+ 2019, 10, "M10", 29);
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from('2019-02-28').subtract({ months: 1 }),
+ 2019, 1, "M01", 28);
+TemporalHelpers.assertPlainDate(date.subtract(Temporal.Duration.from('P43Y')),
+ 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from('1976-11-18').subtract({ years: -43 }),
+ 2019, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from('2018-12-18').subtract({ months: -11 }),
+ 2019, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from('2019-10-29').subtract({ days: -20 }),
+ 2019, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from('2019-01-28').subtract({ months: -1 }),
+ 2019, 2, "M02", 28);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/branding.js
new file mode 100644
index 0000000000..86a63ad288
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const subtract = Temporal.PlainDate.prototype.subtract;
+
+assert.sameValue(typeof subtract, "function");
+
+const args = [new Temporal.Duration(5)];
+
+assert.throws(TypeError, () => subtract.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => subtract.apply(null, args), "null");
+assert.throws(TypeError, () => subtract.apply(true, args), "true");
+assert.throws(TypeError, () => subtract.apply("", args), "empty string");
+assert.throws(TypeError, () => subtract.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => subtract.apply(1, args), "1");
+assert.throws(TypeError, () => subtract.apply({}, args), "plain object");
+assert.throws(TypeError, () => subtract.apply(Temporal.PlainDate, args), "Temporal.PlainDate");
+assert.throws(TypeError, () => subtract.apply(Temporal.PlainDate.prototype, args), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..3eacf11a43
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateAddOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateAdd");
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateAdd should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.subtract(new Temporal.Duration(1));
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", dateAddOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/builtin.js
new file mode 100644
index 0000000000..b0eeade73a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: >
+ Tests that Temporal.PlainDate.prototype.subtract
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.subtract),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.subtract),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.subtract),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.subtract.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/calendar-invalid-return.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/calendar-invalid-return.js
new file mode 100644
index 0000000000..57c5dfcee1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/calendar-invalid-return.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Throw when the returned value from the calendar's dateAdd method is not a PlainDate.
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor(value) {
+ super("iso8601");
+ this.value = value;
+ }
+ dateAdd() {
+ return this.value;
+ }
+}
+
+const tests = [
+ [undefined],
+ [null, "null"],
+ [true],
+ ["2000-05"],
+ [Symbol()],
+ [200005],
+ [200005n],
+ [{}, "plain object"],
+ [() => {}, "lambda"],
+ [Temporal.PlainDate, "Temporal.PlainDate"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype"],
+];
+for (const [test, description = typeof test] of tests) {
+ const plainDate = new Temporal.PlainDate(2000, 5, 2, new CustomCalendar(test));
+ assert.throws(TypeError, () => plainDate.subtract({ years: 1 }), `Expected error with ${description}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/custom.js
new file mode 100644
index 0000000000..06ac7f743d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/custom.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Basic tests with custom calendar
+includes: [compareArray.js,temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const result = new Temporal.PlainDate(1920, 5, 3);
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(...args) {
+ ++calls;
+ assert.sameValue(args.length, 3, "Three arguments");
+ assert.sameValue(args[0], plainDate, "First argument");
+ TemporalHelpers.assertDuration(args[1], -43, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Second argument");
+ assert.sameValue(typeof args[2], "object", "Third argument: type");
+ assert.sameValue(Object.getPrototypeOf(args[2]), null, "Third argument: prototype");
+ assert.compareArray(Object.keys(args[2]), [], "Third argument: keys");
+ return result;
+ }
+}
+const calendar = new CustomCalendar();
+const plainDate = new Temporal.PlainDate(1976, 11, 18, calendar);
+assert.sameValue(plainDate.subtract({ years: 43 }), result);
+assert.sameValue(calls, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..be3aed422b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainDate.prototype.subtract throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.plaindate.prototype.subtract
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainDate.from({ year: 2000, month: 5, day: 2 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/length.js
new file mode 100644
index 0000000000..e430147b69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Temporal.PlainDate.prototype.subtract.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.subtract, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/limits.js
new file mode 100644
index 0000000000..ec4378ed8f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/limits.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainDate.prototype.subtract throws a RangeError if the calculation crosses a limit
+esid: sec-temporal.plaindate.prototype.subtract
+features: [Temporal]
+---*/
+
+const min = Temporal.PlainDate.from("-271821-04-19");
+const max = Temporal.PlainDate.from("+275760-09-13");
+["reject", "constrain"].forEach((overflow) => {
+ assert.throws(RangeError, () => min.subtract({ days: 1 }, { overflow }), `min with ${overflow}`);
+ assert.throws(RangeError, () => max.subtract({ days: -1 }, { overflow }), `max with ${overflow}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/name.js
new file mode 100644
index 0000000000..59ca6ea774
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Temporal.PlainDate.prototype.subtract.name is "subtract".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.subtract, "name", {
+ value: "subtract",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..7f67ed0839
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainDate.prototype.subtract throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.plaindate.prototype.subtract
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainDate.from({ year: 2000, month: 5, day: 2 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: -Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/non-integer-throws-rangeerror.js
new file mode 100644
index 0000000000..a6a93fbf73
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/non-integer-throws-rangeerror.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.subtract({ [field]: -1.5 }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/not-a-constructor.js
new file mode 100644
index 0000000000..47123f35d1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: >
+ Temporal.PlainDate.prototype.subtract does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.subtract();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.subtract), false,
+ "isConstructor(Temporal.PlainDate.prototype.subtract)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/options-object.js
new file mode 100644
index 0000000000..e365c0edd0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/options-object.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const result1 = instance.subtract({ months: 1 }, {});
+TemporalHelpers.assertPlainDate(
+ result1, 2000, 4, "M04", 2,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.subtract({ months: 1 }, () => {});
+TemporalHelpers.assertPlainDate(
+ result2, 2000, 4, "M04", 2,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/options-undefined.js
new file mode 100644
index 0000000000..ecc573fe5b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/options-undefined.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 3, 31);
+const duration = { months: 1 };
+
+const explicit = date.subtract(duration, undefined);
+assert.sameValue(explicit.month, 2, "default overflow is constrain");
+assert.sameValue(explicit.day, 29, "default overflow is constrain");
+
+const implicit = date.subtract(duration);
+assert.sameValue(implicit.month, 2, "default overflow is constrain");
+assert.sameValue(implicit.day, 29, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/options-wrong-type.js
new file mode 100644
index 0000000000..ae2d8a5e1f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/options-wrong-type.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.subtract({ months: 1 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/order-of-operations.js
new file mode 100644
index 0000000000..b3bcb378aa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/order-of-operations.js
@@ -0,0 +1,129 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Properties on an object passed to subtract() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalDurationRecord
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.months.valueOf",
+ "call fields.months.valueOf",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.weeks.valueOf",
+ "call fields.weeks.valueOf",
+ "get fields.years",
+ "get fields.years.valueOf",
+ "call fields.years.valueOf",
+ // AddDate
+ "get this.calendar.dateAdd",
+ "call this.calendar.dateAdd",
+ // inside Calendar.p.dateAdd
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ overflow: "constrain",
+}, "options");
+
+instance.subtract(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+actual.splice(0); // clear
+
+const noCalendarExpected = [
+ // ToTemporalDurationRecord
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.years",
+ "get this.calendar.dateAdd",
+ // AddDate
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+
+const noCalendarFields = TemporalHelpers.propertyBagObserver(actual, {
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+instance.subtract(noCalendarFields, options);
+assert.compareArray(actual, noCalendarExpected, "order of operations with no calendar operation");
+
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-constrain.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-constrain.js
new file mode 100644
index 0000000000..b95f00c8a6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-constrain.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Constrains with overflow constrain
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const mar31 = Temporal.PlainDate.from("2020-03-31");
+TemporalHelpers.assertPlainDate(mar31.subtract({ months: 1 }, { overflow: "constrain" }),
+ 2020, 2, "M02", 29);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-invalid-string.js
new file mode 100644
index 0000000000..625e8b02be
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-invalid-string.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.prototype.subtract step 7:
+ 7. Return ? CalendarDateAdd(_temporalDate_.[[Calendar]], _temporalDate_, _negatedDuration_, _options_).
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const duration = new Temporal.Duration(3, 3, 0, 3);
+
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => date.subtract(duration, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-reject.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-reject.js
new file mode 100644
index 0000000000..e64d34f8ab
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-reject.js
@@ -0,0 +1,14 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Throws with overflow reject
+features: [Temporal]
+---*/
+
+const mar31 = Temporal.PlainDate.from("2020-03-31");
+assert.throws(RangeError, () => mar31.subtract({ months: 1 }, { overflow: "reject" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-undefined.js
new file mode 100644
index 0000000000..ae5caf3df9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-undefined.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.prototype.subtract step 7:
+ 7. Return ? CalendarDateAdd(_temporalDate_.[[Calendar]], _temporalDate_, _negatedDuration_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 31);
+const duration = new Temporal.Duration(3, 1);
+
+const explicit = date.subtract(duration, { overflow: undefined });
+TemporalHelpers.assertPlainDate(explicit, 1997, 4, "M04", 30, "default overflow is constrain");
+const implicit = date.subtract(duration, {});
+TemporalHelpers.assertPlainDate(implicit, 1997, 4, "M04", 30, "default overflow is constrain");
+const lambda = date.subtract(duration, {});
+TemporalHelpers.assertPlainDate(lambda, 1997, 4, "M04", 30, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-wrong-type.js
new file mode 100644
index 0000000000..fc1222b4f4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-wrong-type.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.prototype.subtract step 7:
+ 7. Return ? CalendarDateAdd(_temporalDate_.[[Calendar]], _temporalDate_, _negatedDuration_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const duration = new Temporal.Duration(3, 3, 0, 3);
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => date.subtract(duration, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainDate(result, 1997, 1, "M01", 30, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/prop-desc.js
new file mode 100644
index 0000000000..8cf73cfc43
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: The "subtract" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.subtract,
+ "function",
+ "`typeof PlainDate.prototype.subtract` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "subtract", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/subclassing-ignored.js
new file mode 100644
index 0000000000..70dcbfaa75
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/subclassing-ignored.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Objects of a subclass are never created as return values for subtract()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDate,
+ [2000, 5, 2],
+ "subtract",
+ [{ days: 1 }],
+ (result) => TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 1),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/basic.js
new file mode 100644
index 0000000000..b81332353d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/basic.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tojson
+description: Basic behavior for toJSON
+features: [Temporal]
+---*/
+
+const tests = [
+ [new Temporal.PlainDate(1976, 2, 4), "1976-02-04"],
+ [new Temporal.PlainDate(1976, 11, 18), "1976-11-18"],
+];
+
+const options = new Proxy({}, {
+ get() { throw new Test262Error("should not get properties off argument") }
+});
+for (const [datetime, expected] of tests) {
+ assert.sameValue(datetime.toJSON(), expected, "toJSON without argument");
+ assert.sameValue(datetime.toJSON(options), expected, "toJSON with argument");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/branding.js
new file mode 100644
index 0000000000..74cd8d11a3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tojson
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toJSON = Temporal.PlainDate.prototype.toJSON;
+
+assert.sameValue(typeof toJSON, "function");
+
+assert.throws(TypeError, () => toJSON.call(undefined), "undefined");
+assert.throws(TypeError, () => toJSON.call(null), "null");
+assert.throws(TypeError, () => toJSON.call(true), "true");
+assert.throws(TypeError, () => toJSON.call(""), "empty string");
+assert.throws(TypeError, () => toJSON.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toJSON.call(1), "1");
+assert.throws(TypeError, () => toJSON.call({}), "plain object");
+assert.throws(TypeError, () => toJSON.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => toJSON.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..967f21f909
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tojson
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.toJSON();
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/builtin.js
new file mode 100644
index 0000000000..24e1001083
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tojson
+description: >
+ Tests that Temporal.PlainDate.prototype.toJSON
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.toJSON),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.toJSON),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.toJSON),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.toJSON.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/length.js
new file mode 100644
index 0000000000..dd1d659d44
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tojson
+description: Temporal.PlainDate.prototype.toJSON.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.toJSON, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/name.js
new file mode 100644
index 0000000000..e60424bd10
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tojson
+description: Temporal.PlainDate.prototype.toJSON.name is "toJSON".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.toJSON, "name", {
+ value: "toJSON",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/not-a-constructor.js
new file mode 100644
index 0000000000..53116ce170
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tojson
+description: >
+ Temporal.PlainDate.prototype.toJSON does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.toJSON();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.toJSON), false,
+ "isConstructor(Temporal.PlainDate.prototype.toJSON)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/prop-desc.js
new file mode 100644
index 0000000000..ba879c3a2a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tojson
+description: The "toJSON" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.toJSON,
+ "function",
+ "`typeof PlainDate.prototype.toJSON` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "toJSON", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/year-format.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/year-format.js
new file mode 100644
index 0000000000..4d2d63938d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/year-format.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tojson
+description: Verify that the year is appropriately formatted as 4 or 6 digits
+features: [Temporal]
+---*/
+
+let instance = new Temporal.PlainDate(-100000, 12, 3);
+assert.sameValue(instance.toJSON(), "-100000-12-03", "large negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDate(-10000, 4, 5);
+assert.sameValue(instance.toJSON(), "-010000-04-05", "smallest 5-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDate(-9999, 6, 7);
+assert.sameValue(instance.toJSON(), "-009999-06-07", "largest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDate(-1000, 8, 9);
+assert.sameValue(instance.toJSON(), "-001000-08-09", "smallest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDate(-999, 10, 9);
+assert.sameValue(instance.toJSON(), "-000999-10-09", "largest 3-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDate(-1, 8, 7);
+assert.sameValue(instance.toJSON(), "-000001-08-07", "year -1 formatted as 6-digit");
+
+instance = new Temporal.PlainDate(0, 6, 5);
+assert.sameValue(instance.toJSON(), "0000-06-05", "year 0 formatted as 4-digit");
+
+instance = new Temporal.PlainDate(1, 4, 3);
+assert.sameValue(instance.toJSON(), "0001-04-03", "year 1 formatted as 4-digit");
+
+instance = new Temporal.PlainDate(999, 2, 10);
+assert.sameValue(instance.toJSON(), "0999-02-10", "largest 3-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainDate(1000, 1, 23);
+assert.sameValue(instance.toJSON(), "1000-01-23", "smallest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainDate(9999, 4, 5);
+assert.sameValue(instance.toJSON(), "9999-04-05", "largest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainDate(10000, 6, 7);
+assert.sameValue(instance.toJSON(), "+010000-06-07", "smallest 5-digit positive year formatted as 6-digit");
+
+instance = new Temporal.PlainDate(100000, 8, 9);
+assert.sameValue(instance.toJSON(), "+100000-08-09", "large positive year formatted as 6-digit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/branding.js
new file mode 100644
index 0000000000..11881e250e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tolocalestring
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toLocaleString = Temporal.PlainDate.prototype.toLocaleString;
+
+assert.sameValue(typeof toLocaleString, "function");
+
+assert.throws(TypeError, () => toLocaleString.call(undefined), "undefined");
+assert.throws(TypeError, () => toLocaleString.call(null), "null");
+assert.throws(TypeError, () => toLocaleString.call(true), "true");
+assert.throws(TypeError, () => toLocaleString.call(""), "empty string");
+assert.throws(TypeError, () => toLocaleString.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toLocaleString.call(1), "1");
+assert.throws(TypeError, () => toLocaleString.call({}), "plain object");
+assert.throws(TypeError, () => toLocaleString.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => toLocaleString.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..990b1c7470
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tolocalestring
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.toLocaleString();
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/builtin.js
new file mode 100644
index 0000000000..0d5e2c8308
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tolocalestring
+description: >
+ Tests that Temporal.PlainDate.prototype.toLocaleString
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.toLocaleString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.toLocaleString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.toLocaleString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.toLocaleString.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/length.js
new file mode 100644
index 0000000000..560284d39a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tolocalestring
+description: Temporal.PlainDate.prototype.toLocaleString.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.toLocaleString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/name.js
new file mode 100644
index 0000000000..3c37d8082f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tolocalestring
+description: Temporal.PlainDate.prototype.toLocaleString.name is "toLocaleString".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.toLocaleString, "name", {
+ value: "toLocaleString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/not-a-constructor.js
new file mode 100644
index 0000000000..0e2ff764d7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tolocalestring
+description: >
+ Temporal.PlainDate.prototype.toLocaleString does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.toLocaleString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.toLocaleString), false,
+ "isConstructor(Temporal.PlainDate.prototype.toLocaleString)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/prop-desc.js
new file mode 100644
index 0000000000..ae8d974087
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tolocalestring
+description: The "toLocaleString" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.toLocaleString,
+ "function",
+ "`typeof PlainDate.prototype.toLocaleString` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "toLocaleString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/return-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/return-string.js
new file mode 100644
index 0000000000..3843752842
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/return-string.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Kate Miháliková. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tolocalestring
+description: >
+ toLocaleString return a string.
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+
+assert.sameValue(typeof date.toLocaleString("en", { dateStyle: "short" }), "string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-number.js
new file mode 100644
index 0000000000..8c0d46c48f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-number.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: A number is invalid in place of an ISO string for Temporal.PlainTime
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const numbers = [
+ 1,
+ -123456.987654321,
+ 1234567,
+ 123456.9876543219,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.toPlainDateTime(arg),
+ `A number (${arg}) is not a valid ISO string for PlainTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-object.js
new file mode 100644
index 0000000000..950b2b0e49
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-object.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: Tests for toPlainDateTime() with an object argument.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2000, 5, 2);
+const calendar = { toString() { return "iso8601" } };
+const withOverflow = plainDate.toPlainDateTime({ hour: 25, minute: 70, second: 23 });
+TemporalHelpers.assertPlainDateTime(withOverflow, 2000, 5, "M05", 2, 23, 59, 23, 0, 0, 0, "with overflow");
+assert.sameValue(withOverflow.calendar, plainDate.calendar, "with overflow calendar");
+
+const withCalendar = plainDate.toPlainDateTime({ hour: 13, calendar });
+TemporalHelpers.assertPlainDateTime(withCalendar, 2000, 5, "M05", 2, 13, 0, 0, 0, 0, 0, "with calendar");
+assert.sameValue(withCalendar.calendar, plainDate.calendar, "with calendar calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..7b7b86161b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-calendar-annotation.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["12:34:56.987654321[u-ca=iso8601]", "without time zone"],
+ ["12:34:56.987654321[UTC][u-ca=iso8601]", "with time zone"],
+ ["12:34:56.987654321[!u-ca=iso8601]", "with ! and no time zone"],
+ ["12:34:56.987654321[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["T12:34:56.987654321[u-ca=iso8601]", "with T and no time zone"],
+ ["T12:34:56.987654321[UTC][u-ca=iso8601]", "with T and time zone"],
+ ["T12:34:56.987654321[!u-ca=iso8601]", "with T, !, and no time zone"],
+ ["T12:34:56.987654321[UTC][!u-ca=iso8601]", "with T, !, and time zone"],
+ ["1970-01-01T12:34:56.987654321[u-ca=iso8601]", "with date and no time zone"],
+ ["1970-01-01T12:34:56.987654321[UTC][u-ca=iso8601]", "with date and time zone"],
+ ["1970-01-01T12:34:56.987654321[!u-ca=iso8601]", "with !, date, and no time zone"],
+ ["1970-01-01T12:34:56.987654321[UTC][!u-ca=iso8601]", "with !, date, and time zone"],
+ ["12:34:56.987654321[u-ca=hebrew]", "calendar annotation ignored"],
+ ["12:34:56.987654321[u-ca=unknown]", "calendar annotation ignored even if unknown calendar"],
+ ["12:34:56.987654321[!u-ca=unknown]", "calendar annotation ignored even if unknown calendar with !"],
+ ["1970-01-01T12:34:56.987654321[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.toPlainDateTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 321,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..7c3603fb69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "00:00[!foo=bar]",
+ "T00:00[!foo=bar]",
+ "1970-01-01T00:00[!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toPlainDateTime(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..a9af6a6da0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-date-with-utc-offset.js
@@ -0,0 +1,53 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const validStrings = [
+ "12:34:56.987654321+00:00",
+ "12:34:56.987654321+00:00[UTC]",
+ "12:34:56.987654321+00:00[!UTC]",
+ "12:34:56.987654321-02:30[America/St_Johns]",
+ "1976-11-18T12:34:56.987654321+00:00",
+ "1976-11-18T12:34:56.987654321+00:00[UTC]",
+ "1976-11-18T12:34:56.987654321+00:00[!UTC]",
+ "1976-11-18T12:34:56.987654321-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.toPlainDateTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 321,
+ `"${arg}" is a valid UTC offset with time for PlainTime`
+ );
+}
+
+const invalidStrings = [
+ "2022-09-15Z",
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.toPlainDateTime(arg),
+ `"${arg}" UTC offset without time is not valid for PlainTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..320c02eb57
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-multiple-calendar.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toPlainDateTime(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..ab6b4d7001
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-multiple-time-zone.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "00:00[UTC][UTC]",
+ "T00:00[UTC][UTC]",
+ "1970-01-01T00:00[UTC][UTC]",
+ "1970-01-01T00:00[!UTC][UTC]",
+ "1970-01-01T00:00[UTC][!UTC]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00[UTC][foo=bar][UTC]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toPlainDateTime(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-no-implicit-midnight.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-no-implicit-midnight.js
new file mode 100644
index 0000000000..b07a0fe3a6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-no-implicit-midnight.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: RangeError thrown if a date-only string is passed in a PlainTime context
+features: [Temporal, arrow-function]
+---*/
+
+const arg = "2019-10-01";
+const instance = new Temporal.PlainDate(2000, 5, 2);
+assert.throws(
+ RangeError,
+ () => instance.toPlainDateTime(arg),
+ "Date-only string throws, does not implicitly convert to midnight"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-time-designator-required-for-disambiguation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-time-designator-required-for-disambiguation.js
new file mode 100644
index 0000000000..eaecbfeb56
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-time-designator-required-for-disambiguation.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: ISO 8601 time designator "T" required in cases of ambiguity
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+TemporalHelpers.ISO.plainTimeStringsAmbiguous().forEach((string) => {
+ let arg = string;
+ assert.throws(
+ RangeError,
+ () => instance.toPlainDateTime(arg),
+ `'${arg}' is ambiguous and requires T prefix`
+ );
+ // The same string with a T prefix should not throw:
+ arg = `T${string}`;
+ instance.toPlainDateTime(arg);
+
+ arg = ` ${string}`;
+ assert.throws(
+ RangeError,
+ () => instance.toPlainDateTime(arg),
+ `space is not accepted as a substitute for T prefix: '${arg}'`
+ );
+});
+
+// None of these should throw without a T prefix, because they are unambiguously time strings:
+TemporalHelpers.ISO.plainTimeStringsUnambiguous().forEach(
+ (arg) => instance.toPlainDateTime(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-time-separators.js
new file mode 100644
index 0000000000..6819a99c6c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-time-separators.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T12:34:56.987654321", "uppercase T"],
+ ["1976-11-18t12:34:56.987654321", "lowercase T"],
+ ["1976-11-18 12:34:56.987654321", "space between date and time"],
+ ["T12:34:56.987654321", "time-only uppercase T"],
+ ["t12:34:56.987654321", "time-only lowercase T"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.toPlainDateTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 321,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..fea520d08d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-time-zone-annotation.js
@@ -0,0 +1,51 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["12:34:56.987654321[Asia/Kolkata]", "named, with no offset"],
+ ["12:34:56.987654321[!Europe/Vienna]", "named, with ! and no offset"],
+ ["12:34:56.987654321[+00:00]", "numeric, with no offset"],
+ ["12:34:56.987654321[!-02:30]", "numeric, with ! and no offset"],
+ ["T12:34:56.987654321[UTC]", "named, with T and no offset"],
+ ["T12:34:56.987654321[!Africa/Abidjan]", "named, with T, !, and no offset"],
+ ["T12:34:56.987654321[+01:00]", "numeric, with T and no offset"],
+ ["T12:34:56.987654321[!-08:00]", "numeric, with T, !, and no offset"],
+ ["12:34:56.987654321+00:00[America/Sao_Paulo]", "named, with offset"],
+ ["12:34:56.987654321+00:00[!Asia/Tokyo]", "named, with ! and offset"],
+ ["12:34:56.987654321+00:00[-02:30]", "numeric, with offset"],
+ ["12:34:56.987654321+00:00[!+00:00]", "numeric, with ! and offset"],
+ ["T12:34:56.987654321+00:00[America/New_York]", "named, with T and offset"],
+ ["T12:34:56.987654321+00:00[!UTC]", "named, with T, !, and offset"],
+ ["T12:34:56.987654321+00:00[-08:00]", "numeric, with T and offset"],
+ ["T12:34:56.987654321+00:00[!+01:00]", "numeric, with T, !, and offset"],
+ ["1970-01-01T12:34:56.987654321[Africa/Lagos]", "named, with date and no offset"],
+ ["1970-01-01T12:34:56.987654321[!America/Vancouver]", "named, with date, !, and no offset"],
+ ["1970-01-01T12:34:56.987654321[+00:00]", "numeric, with date and no offset"],
+ ["1970-01-01T12:34:56.987654321[!-02:30]", "numeric, with date, !, and no offset"],
+ ["1970-01-01T12:34:56.987654321+00:00[Europe/London]", "named, with date and offset"],
+ ["1970-01-01T12:34:56.987654321+00:00[!Asia/Seoul]", "named, with date, offset, and !"],
+ ["1970-01-01T12:34:56.987654321+00:00[+01:00]", "numeric, with date and offset"],
+ ["1970-01-01T12:34:56.987654321+00:00[!-08:00]", "numeric, with date, offset, and !"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.toPlainDateTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 321,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..229b3c496a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-unknown-annotation.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["12:34:56.987654321[foo=bar]", "alone"],
+ ["12:34:56.987654321[UTC][foo=bar]", "with time zone"],
+ ["12:34:56.987654321[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["T12:34:56.987654321[foo=bar]", "with T"],
+ ["T12:34:56.987654321[UTC][foo=bar]", "with T and time zone"],
+ ["T12:34:56.987654321[u-ca=iso8601][foo=bar]", "with T and calendar"],
+ ["T12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with T, time zone, and calendar"],
+ ["1970-01-01T12:34:56.987654321[foo=bar]", "with date"],
+ ["1970-01-01T12:34:56.987654321[UTC][foo=bar]", "with date and time zone"],
+ ["1970-01-01T12:34:56.987654321[u-ca=iso8601][foo=bar]", "with date and calendar"],
+ ["1970-01-01T12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with date, time zone, and calendar"],
+ ["1970-01-01T12:34:56.987654321[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.toPlainDateTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 321,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-with-time-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-with-time-designator.js
new file mode 100644
index 0000000000..b4911b666e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-with-time-designator.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: ISO 8601 time designator "T" allowed at the start of PlainTime strings
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 1, 1);
+const validStrings = [
+ "T00:30",
+ "t00:30",
+ "T0030",
+ "t0030",
+ "T00:30:00",
+ "t00:30:00",
+ "T003000",
+ "t003000",
+ "T00:30:00.000000000",
+ "t00:30:00.000000000",
+ "T003000.000000000",
+ "t003000.000000000",
+];
+validStrings.forEach((arg) => {
+ const result = instance.toPlainDateTime(arg);
+ TemporalHelpers.assertPlainDateTime(result, 2000, 1, "M01", 1, 0, 30, 0, 0, 0, 0, `T prefix is accepted: ${arg}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..889739cf59
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-with-utc-designator.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: RangeError thrown if a string with UTC designator is used as a PlainTime
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+ "09:00:00Z[UTC]",
+ "09:00:00Z",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toPlainDateTime(arg),
+ "String with UTC designator should not be valid as a PlainTime"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-wrong-type.js
new file mode 100644
index 0000000000..9debfc601d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-wrong-type.js
@@ -0,0 +1,42 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainTime
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => instance.toPlainDateTime(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainTime, "Temporal.PlainTime, object"],
+ [Temporal.PlainTime.prototype, "Temporal.PlainTime.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.toPlainDateTime(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 0000000000..964d7d6c68
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,47 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: Negative time fields are balanced upwards if the argument is given as ZonedDateTime
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-temporal-totemporaltime step 3.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ ii. 1. Set _plainDateTime_ to ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
+ sec-temporal.plaindate.prototype.toplaindatetime step 4:
+ 4. Set _temporalTime_ to ? ToTemporalTime(_temporalTime_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// This code path is encountered if the time zone offset is negative and its
+// absolute value in nanoseconds is greater than the nanosecond field of the
+// exact time's epoch parts
+const tz = TemporalHelpers.specificOffsetTimeZone(-2);
+const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const pdt = date.toPlainDateTime(datetime);
+
+TemporalHelpers.assertPlainDateTime(pdt, 2000, 5, "M05", 2, 1, 1, 1, 1, 0, 999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..19fb21bc72
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const result = instance.toPlainDateTime(datetime);
+TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 16, 50, 35, 0, 0, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..e969e0d72d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.toPlainDateTime(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..350e9e3644
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => date.toPlainDateTime(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..8145f3160e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.toPlainDateTime(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..32fcf358d6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => date.toPlainDateTime(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/basic.js
new file mode 100644
index 0000000000..ee3f828471
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/basic.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: Basic tests for toPlainDateTime().
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+
+const string = date.toPlainDateTime("11:30:23");
+TemporalHelpers.assertPlainDateTime(string, 2000, 5, "M05", 2, 11, 30, 23, 0, 0, 0, "string");
+assert.sameValue(string.calendar, date.calendar, "string calendar");
+
+const optionBag = date.toPlainDateTime({ hour: 11, minute: 30, second: 23 });
+TemporalHelpers.assertPlainDateTime(optionBag, 2000, 5, "M05", 2, 11, 30, 23, 0, 0, 0, "option bag");
+assert.sameValue(optionBag.calendar, date.calendar, "option bag calendar");
+
+const plainTime = date.toPlainDateTime(Temporal.PlainTime.from("11:30:23"));
+TemporalHelpers.assertPlainDateTime(plainTime, 2000, 5, "M05", 2, 11, 30, 23, 0, 0, 0, "PlainTime");
+assert.sameValue(plainTime.calendar, date.calendar, "PlainTime calendar");
+
+const plainDateTime = date.toPlainDateTime(Temporal.PlainDateTime.from("1999-07-14T11:30:23"));
+TemporalHelpers.assertPlainDateTime(plainDateTime, 2000, 5, "M05", 2, 11, 30, 23, 0, 0, 0, "PlainDateTime");
+assert.sameValue(plainDateTime.calendar, date.calendar, "PlainDateTime calendar");
+
+const zonedDateTime = date.toPlainDateTime(Temporal.ZonedDateTime.from("1999-07-14T11:30:23Z[UTC]"));
+TemporalHelpers.assertPlainDateTime(zonedDateTime, 2000, 5, "M05", 2, 11, 30, 23, 0, 0, 0, "ZonedDateTime");
+assert.sameValue(zonedDateTime.calendar, date.calendar, "ZonedDateTime calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/branding.js
new file mode 100644
index 0000000000..4e05ce4608
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toPlainDateTime = Temporal.PlainDate.prototype.toPlainDateTime;
+
+assert.sameValue(typeof toPlainDateTime, "function");
+
+assert.throws(TypeError, () => toPlainDateTime.call(undefined), "undefined");
+assert.throws(TypeError, () => toPlainDateTime.call(null), "null");
+assert.throws(TypeError, () => toPlainDateTime.call(true), "true");
+assert.throws(TypeError, () => toPlainDateTime.call(""), "empty string");
+assert.throws(TypeError, () => toPlainDateTime.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toPlainDateTime.call(1), "1");
+assert.throws(TypeError, () => toPlainDateTime.call({}), "plain object");
+assert.throws(TypeError, () => toPlainDateTime.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => toPlainDateTime.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/builtin.js
new file mode 100644
index 0000000000..e3a19cdf87
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: >
+ Tests that Temporal.PlainDate.prototype.toPlainDateTime
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.toPlainDateTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.toPlainDateTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.toPlainDateTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.toPlainDateTime.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/custom.js
new file mode 100644
index 0000000000..feecd145ab
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/custom.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: toPlainDateTime() doesn't call into the calendar.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarThrowEverything();
+const plainDate = new Temporal.PlainDate(2000, 5, 2, calendar);
+const result = plainDate.toPlainDateTime("11:30:23");
+assert.sameValue(result.getCalendar(), calendar, "calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/leap-second.js
new file mode 100644
index 0000000000..0e51120afe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/leap-second.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: Leap second is a valid ISO string for PlainTime
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.toPlainDateTime(arg);
+TemporalHelpers.assertPlainDateTime(
+ result1,
+ 2000, 5, "M05", 2, 23, 59, 59, 0, 0, 0,
+ "leap second is a valid ISO string for PlainTime"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.toPlainDateTime(arg);
+TemporalHelpers.assertPlainDateTime(
+ result2,
+ 2000, 5, "M05", 2, 23, 59, 59, 0, 0, 0,
+ "second: 60 is ignored in property bag for PlainTime"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/length.js
new file mode 100644
index 0000000000..b379b4487f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: Temporal.PlainDate.prototype.toPlainDateTime.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.toPlainDateTime, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/limits.js
new file mode 100644
index 0000000000..ef00cbc673
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/limits.js
@@ -0,0 +1,42 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: Checking limits of representable PlainDateTime
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const midnight = new Temporal.PlainTime(0, 0);
+const firstNs = new Temporal.PlainTime(0, 0, 0, 0, 0, 1);
+const lastNs = new Temporal.PlainTime(23, 59, 59, 999, 999, 999);
+const min = new Temporal.PlainDate(-271821, 4, 19);
+const max = new Temporal.PlainDate(275760, 9, 13);
+
+assert.throws(
+ RangeError,
+ () => min.toPlainDateTime(midnight),
+ "Cannot go below representable limit for PlainDateTime"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ max.toPlainDateTime(midnight),
+ 275760, 9, "M09", 13, 0, 0, 0, 0, 0, 0,
+ "Midnight on maximal representable PlainDate"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ min.toPlainDateTime(firstNs),
+ -271821, 4, "M04", 19, 0, 0, 0, 0, 0, 1,
+ "Computing the minimum (earliest) representable PlainDateTime"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ max.toPlainDateTime(lastNs),
+ 275760, 9, "M09", 13, 23, 59, 59, 999, 999, 999,
+ "Computing the maximum (latest) representable PlainDateTime"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/name.js
new file mode 100644
index 0000000000..6602a260cc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: Temporal.PlainDate.prototype.toPlainDateTime.name is "toPlainDateTime".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.toPlainDateTime, "name", {
+ value: "toPlainDateTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/not-a-constructor.js
new file mode 100644
index 0000000000..1c7ef2a91e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: >
+ Temporal.PlainDate.prototype.toPlainDateTime does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.toPlainDateTime();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.toPlainDateTime), false,
+ "isConstructor(Temporal.PlainDate.prototype.toPlainDateTime)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/plaintime-propertybag-no-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/plaintime-propertybag-no-time-units.js
new file mode 100644
index 0000000000..0c2dc3a98e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/plaintime-propertybag-no-time-units.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: Missing time units in property bag default to 0
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 1, 1);
+
+const props = {};
+assert.throws(TypeError, () => instance.toPlainDateTime(props), "TypeError if no properties are present");
+
+props.minute = 30;
+const result = instance.toPlainDateTime(props);
+TemporalHelpers.assertPlainDateTime(result, 2000, 1, "M01", 1, 0, 30, 0, 0, 0, 0, "missing time units default to 0");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/prop-desc.js
new file mode 100644
index 0000000000..4d1804f704
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: The "toPlainDateTime" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.toPlainDateTime,
+ "function",
+ "`typeof PlainDate.prototype.toPlainDateTime` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "toPlainDateTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/time-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/time-invalid.js
new file mode 100644
index 0000000000..410d715a42
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/time-invalid.js
@@ -0,0 +1,15 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: TypeError thrown if an invalid property bag passed
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2000, 5, 2);
+assert.throws(TypeError, () => plainDate.toPlainDateTime({}), "empty object");
+assert.throws(TypeError, () => plainDate.toPlainDateTime({ minutes: 30 }), "plural property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/time-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/time-undefined.js
new file mode 100644
index 0000000000..19b2c1148a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/time-undefined.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: The time is assumed to be midnight if not given
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+
+const explicit = date.toPlainDateTime(undefined);
+TemporalHelpers.assertPlainDateTime(explicit, 2000, 5, "M05", 2, 0, 0, 0, 0, 0, 0, "default time is midnight - explicit");
+
+const implicit = date.toPlainDateTime();
+TemporalHelpers.assertPlainDateTime(implicit, 2000, 5, "M05", 2, 0, 0, 0, 0, 0, 0, "default time is midnight - implicit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/year-zero.js
new file mode 100644
index 0000000000..d91e610a16
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/year-zero.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-12-07T03:24:30",
+ "-000000-12-07T03:24:30+01:00",
+ "-000000-12-07T03:24:30+00:00[UTC]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toPlainDateTime(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/basic.js
new file mode 100644
index 0000000000..8913418dd4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/basic.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainmonthday
+description: Basic toPlainMonthDay tests.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+const pd = new Temporal.PlainDate(1970, 12, 24, calendar);
+const pmd = pd.toPlainMonthDay();
+TemporalHelpers.assertPlainMonthDay(pmd, "M12", 24);
+assert.sameValue(pmd.getISOFields().calendar, "iso8601");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/branding.js
new file mode 100644
index 0000000000..7e8ac4ce94
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainmonthday
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toPlainMonthDay = Temporal.PlainDate.prototype.toPlainMonthDay;
+
+assert.sameValue(typeof toPlainMonthDay, "function");
+
+assert.throws(TypeError, () => toPlainMonthDay.call(undefined), "undefined");
+assert.throws(TypeError, () => toPlainMonthDay.call(null), "null");
+assert.throws(TypeError, () => toPlainMonthDay.call(true), "true");
+assert.throws(TypeError, () => toPlainMonthDay.call(""), "empty string");
+assert.throws(TypeError, () => toPlainMonthDay.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toPlainMonthDay.call(1), "1");
+assert.throws(TypeError, () => toPlainMonthDay.call({}), "plain object");
+assert.throws(TypeError, () => toPlainMonthDay.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => toPlainMonthDay.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..b6551cc78e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainmonthday
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainDate(2023, 5, 2, "iso8601");
+instance.toPlainMonthDay();
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..415b89e0c6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainmonthday
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const monthDayFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "monthDayFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "monthDayFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("monthDayFromFields should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.toPlainMonthDay();
+
+Object.defineProperty(Temporal.Calendar.prototype, "monthDayFromFields", monthDayFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/builtin.js
new file mode 100644
index 0000000000..1c6fce388a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainmonthday
+description: >
+ Tests that Temporal.PlainDate.prototype.toPlainMonthDay
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.toPlainMonthDay),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.toPlainMonthDay),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.toPlainMonthDay),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.toPlainMonthDay.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-arguments.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-arguments.js
new file mode 100644
index 0000000000..0c4cec8683
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-arguments.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainmonthday
+description: Correct options value is passed to calendar method
+info: |
+ MonthDayFromFields ( calendar, fields [ , options ] )
+
+ 3. If options is not present, then
+ a. Set options to undefined.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ monthDayFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.sameValue(args[1], undefined, "args[1]");
+ return super.monthDayFromFields(...args);
+ }
+}
+const plainDate = new Temporal.PlainDate(2000, 5, 2, new CustomCalendar());
+const result = plainDate.toPlainMonthDay();
+TemporalHelpers.assertPlainMonthDay(result, "M05", 2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-fields-iterable.js
new file mode 100644
index 0000000000..04a6061fe0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-fields-iterable.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainmonthday
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindate.prototype.toplainmonthday step 4:
+ 4. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"monthCode"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "monthCode",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const date = new Temporal.PlainDate(2000, 5, 2, calendar);
+date.toPlainMonthDay();
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-fromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..ed596ad4a1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-fromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainmonthday
+description: >
+ Calendar.monthDayFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+instance.toPlainMonthDay();
+assert.sameValue(calendar.monthDayFromFieldsCallCount, 1, "monthDayFromFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-invalid-return.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-invalid-return.js
new file mode 100644
index 0000000000..8bab7f5cdf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-invalid-return.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainmonthday
+description: Throw when the returned value is not a PlainMonthDay.
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor(value) {
+ super("iso8601");
+ this.value = value;
+ }
+ monthDayFromFields() {
+ return this.value;
+ }
+}
+
+const tests = [
+ [undefined],
+ [null, "null"],
+ [true],
+ ["2000-05"],
+ [Symbol()],
+ [200005],
+ [200005n],
+ [{}, "plain object"],
+ [() => {}, "lambda"],
+ [Temporal.PlainMonthDay, "Temporal.PlainMonthDay"],
+ [Temporal.PlainMonthDay.prototype, "Temporal.PlainMonthDay.prototype"],
+];
+for (const [test, description = typeof test] of tests) {
+ const plainDate = new Temporal.PlainDate(2000, 5, 2, new CustomCalendar(test));
+ assert.throws(TypeError, () => plainDate.toPlainMonthDay(), `Expected error with ${description}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-monthdayfromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-monthdayfromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..2c1fbc5862
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-monthdayfromfields-called-with-options-undefined.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainmonthday
+description: >
+ Calendar.monthDayFromFields method is called with undefined as the options
+ value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+instance.toPlainMonthDay();
+assert.sameValue(calendar.monthDayFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..1151eadc13
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindate.prototype.toplainmonthday
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const date = new Temporal.PlainDate(2023, 5, 1, calendar);
+
+assert.throws(RangeError, () => date.toPlainMonthDay());
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..3467871850
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/duplicate-calendar-fields.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindate.prototype.toplainmonthday
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['monthCode'], ['day']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const date = new Temporal.PlainDate(2023, 5, 1, calendar);
+
+ assert.throws(RangeError, () => date.toPlainMonthDay());
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/length.js
new file mode 100644
index 0000000000..e86efba232
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainmonthday
+description: Temporal.PlainDate.prototype.toPlainMonthDay.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.toPlainMonthDay, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/name.js
new file mode 100644
index 0000000000..6df4fb2d10
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainmonthday
+description: Temporal.PlainDate.prototype.toPlainMonthDay.name is "toPlainMonthDay".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.toPlainMonthDay, "name", {
+ value: "toPlainMonthDay",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/not-a-constructor.js
new file mode 100644
index 0000000000..0c64846eef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainmonthday
+description: >
+ Temporal.PlainDate.prototype.toPlainMonthDay does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.toPlainMonthDay();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.toPlainMonthDay), false,
+ "isConstructor(Temporal.PlainDate.prototype.toPlainMonthDay)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/prop-desc.js
new file mode 100644
index 0000000000..e6d11cf65c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainmonthday
+description: The "toPlainMonthDay" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.toPlainMonthDay,
+ "function",
+ "`typeof PlainDate.prototype.toPlainMonthDay` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "toPlainMonthDay", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..d77d73bef1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindate.prototype.toplainmonthday
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const date = new Temporal.PlainDate(2023, 5, 1, calendar);
+
+assert.throws(RangeError, () => date.toPlainMonthDay());
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/basic.js
new file mode 100644
index 0000000000..f0375df0d3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/basic.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainyearmonth
+description: Basic toPlainYearMonth tests.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+const pd = new Temporal.PlainDate(1970, 12, 24, calendar);
+const pym = pd.toPlainYearMonth();
+TemporalHelpers.assertPlainYearMonth(pym, 1970, 12, "M12");
+assert.sameValue(pym.getISOFields().calendar, "iso8601");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/branding.js
new file mode 100644
index 0000000000..474a20edae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainyearmonth
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toPlainYearMonth = Temporal.PlainDate.prototype.toPlainYearMonth;
+
+assert.sameValue(typeof toPlainYearMonth, "function");
+
+assert.throws(TypeError, () => toPlainYearMonth.call(undefined), "undefined");
+assert.throws(TypeError, () => toPlainYearMonth.call(null), "null");
+assert.throws(TypeError, () => toPlainYearMonth.call(true), "true");
+assert.throws(TypeError, () => toPlainYearMonth.call(""), "empty string");
+assert.throws(TypeError, () => toPlainYearMonth.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toPlainYearMonth.call(1), "1");
+assert.throws(TypeError, () => toPlainYearMonth.call({}), "plain object");
+assert.throws(TypeError, () => toPlainYearMonth.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => toPlainYearMonth.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..120d0e59a4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainyearmonth
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainDate(2023, 5, 2, "iso8601");
+instance.toPlainYearMonth();
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..1254551e64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainyearmonth
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const yearMonthFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "yearMonthFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "yearMonthFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("yearMonthFromFields should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.toPlainYearMonth();
+
+Object.defineProperty(Temporal.Calendar.prototype, "yearMonthFromFields", yearMonthFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/builtin.js
new file mode 100644
index 0000000000..2aaa7857cb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainyearmonth
+description: >
+ Tests that Temporal.PlainDate.prototype.toPlainYearMonth
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.toPlainYearMonth),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.toPlainYearMonth),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.toPlainYearMonth),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.toPlainYearMonth.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-arguments.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-arguments.js
new file mode 100644
index 0000000000..af062b81de
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-arguments.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainyearmonth
+description: Correct options value is passed to calendar method
+info: |
+ YearMonthFromFields ( calendar, fields [ , options ] )
+
+ 3. If options is not present, then
+ a. Set options to undefined.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ yearMonthFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.sameValue(args[1], undefined, "args[1]");
+ return super.yearMonthFromFields(...args);
+ }
+}
+const plainDate = new Temporal.PlainDate(2000, 5, 2, new CustomCalendar());
+const result = plainDate.toPlainYearMonth();
+TemporalHelpers.assertPlainYearMonth(result, 2000, 5, "M05");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-fields-iterable.js
new file mode 100644
index 0000000000..60d60b3ba5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-fields-iterable.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainyearmonth
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindate.prototype.toplainyearmonth step 4:
+ 4. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const date = new Temporal.PlainDate(2000, 5, 2, calendar);
+date.toPlainYearMonth();
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-fromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..c22c2e0f28
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-fromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainyearmonth
+description: >
+ Calendar.yearMonthFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+instance.toPlainYearMonth();
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-invalid-return.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-invalid-return.js
new file mode 100644
index 0000000000..79fdfa229c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-invalid-return.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainyearmonth
+description: Throw when the returned value is not a PlainYearMonth.
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor(value) {
+ super("iso8601");
+ this.value = value;
+ }
+ yearMonthFromFields() {
+ return this.value;
+ }
+}
+
+const tests = [
+ [undefined],
+ [null, "null"],
+ [true],
+ ["2000-05"],
+ [Symbol()],
+ [200005],
+ [200005n],
+ [{}, "plain object"],
+ [() => {}, "lambda"],
+ [Temporal.PlainYearMonth, "Temporal.PlainYearMonth"],
+ [Temporal.PlainYearMonth.prototype, "Temporal.PlainYearMonth.prototype"],
+];
+for (const [test, description = typeof test] of tests) {
+ const plainDate = new Temporal.PlainDate(2000, 5, 2, new CustomCalendar(test));
+ assert.throws(TypeError, () => plainDate.toPlainYearMonth(), `Expected error with ${description}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-yearmonthfromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-yearmonthfromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..71c8e2f012
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-yearmonthfromfields-called-with-options-undefined.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainyearmonth
+description: >
+ Calendar.yearMonthFromFields method is called with undefined as the options
+ value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+instance.toPlainYearMonth();
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..25e3db2553
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindate.prototype.toplainyearmonth
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const date = new Temporal.PlainDate(2023, 5, 1, calendar);
+
+assert.throws(RangeError, () => date.toPlainYearMonth());
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..31e21202a1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/duplicate-calendar-fields.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindate.prototype.toplainyearmonth
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const date = new Temporal.PlainDate(2023, 5, 1, calendar);
+
+ assert.throws(RangeError, () => date.toPlainYearMonth());
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/length.js
new file mode 100644
index 0000000000..c5166053c9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainyearmonth
+description: Temporal.PlainDate.prototype.toPlainYearMonth.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.toPlainYearMonth, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/limits.js
new file mode 100644
index 0000000000..e45d4b90b8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/limits.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainyearmonth
+description: toPlainYearMonth works within the supported range
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const min = Temporal.PlainDate.from('-271821-04-19');
+TemporalHelpers.assertPlainYearMonth(min.toPlainYearMonth(),
+ -271821, 4, "M04", "min");
+
+const max = Temporal.PlainDate.from('+275760-09-13');
+TemporalHelpers.assertPlainYearMonth(max.toPlainYearMonth(),
+ 275760, 9, "M09", "max");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/name.js
new file mode 100644
index 0000000000..219f4134e7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainyearmonth
+description: Temporal.PlainDate.prototype.toPlainYearMonth.name is "toPlainYearMonth".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.toPlainYearMonth, "name", {
+ value: "toPlainYearMonth",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/not-a-constructor.js
new file mode 100644
index 0000000000..cd849a5ec4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainyearmonth
+description: >
+ Temporal.PlainDate.prototype.toPlainYearMonth does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.toPlainYearMonth();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.toPlainYearMonth), false,
+ "isConstructor(Temporal.PlainDate.prototype.toPlainYearMonth)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/prop-desc.js
new file mode 100644
index 0000000000..b57bfe6a1f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainyearmonth
+description: The "toPlainYearMonth" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.toPlainYearMonth,
+ "function",
+ "`typeof PlainDate.prototype.toPlainYearMonth` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "toPlainYearMonth", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..91397b7c51
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindate.prototype.toplainyearmonth
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const date = new Temporal.PlainDate(2023, 5, 1, calendar);
+
+assert.throws(RangeError, () => date.toPlainYearMonth());
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/basic.js
new file mode 100644
index 0000000000..1b856747c0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/basic.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.protoype.tostring
+description: basic tests
+features: [Temporal]
+---*/
+
+const date1 = new Temporal.PlainDate(1976, 11, 18);
+assert.sameValue(date1.toString(), "1976-11-18");
+
+const date2 = new Temporal.PlainDate(1914, 2, 23);
+assert.sameValue(date2.toString(), "1914-02-23");
+
+const date3 = new Temporal.PlainDate(1996, 2, 29);
+assert.sameValue(date3.toString(), "1996-02-29");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/branding.js
new file mode 100644
index 0000000000..eb8f0cd16b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tostring
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toString = Temporal.PlainDate.prototype.toString;
+
+assert.sameValue(typeof toString, "function");
+
+assert.throws(TypeError, () => toString.call(undefined), "undefined");
+assert.throws(TypeError, () => toString.call(null), "null");
+assert.throws(TypeError, () => toString.call(true), "true");
+assert.throws(TypeError, () => toString.call(""), "empty string");
+assert.throws(TypeError, () => toString.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toString.call(1), "1");
+assert.throws(TypeError, () => toString.call({}), "plain object");
+assert.throws(TypeError, () => toString.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => toString.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..0ce71025f7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tostring
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.toString();
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/builtin.js
new file mode 100644
index 0000000000..070c996b91
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tostring
+description: >
+ Tests that Temporal.PlainDate.prototype.toString
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.toString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.toString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.toString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.toString.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendar-tostring.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendar-tostring.js
new file mode 100644
index 0000000000..b09d756a6d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendar-tostring.js
@@ -0,0 +1,56 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.protoype.tostring
+description: Number of observable 'toString' calls on the calendar for each value of calendarName
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let calls;
+const customCalendar = {
+ get id() {
+ ++calls;
+ return "custom";
+ },
+ toString() {
+ TemporalHelpers.assertUnreachable('toString should not be called');
+ },
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const date = new Temporal.PlainDate(2000, 5, 2, customCalendar);
+[
+ ["always", "2000-05-02[u-ca=custom]", 1],
+ ["auto", "2000-05-02[u-ca=custom]", 1],
+ ["critical", "2000-05-02[!u-ca=custom]", 1],
+ ["never", "2000-05-02", 0],
+ [undefined, "2000-05-02[u-ca=custom]", 1],
+].forEach(([calendarName, expectedResult, expectedCalls]) => {
+ calls = 0;
+ const result = date.toString({ calendarName });
+ assert.sameValue(result, expectedResult, `id for calendarName = ${calendarName}`);
+ assert.sameValue(calls, expectedCalls, `calls to id getter for calendarName = ${calendarName}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-always.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-always.js
new file mode 100644
index 0000000000..686b38ffbf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-always.js
@@ -0,0 +1,48 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tostring
+description: If calendarName is "always", the calendar ID should be included.
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "2000-05-02[u-ca=iso8601]", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "2000-05-02[u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "2000-05-02[u-ca=iso8601]", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "2000-05-02[u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "2000-05-02[u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const date = new Temporal.PlainDate(2000, 5, 2, ...args);
+ const result = date.toString({ calendarName: "always" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = always`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-auto.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-auto.js
new file mode 100644
index 0000000000..7b5e322e18
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-auto.js
@@ -0,0 +1,48 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tostring
+description: If calendarName is "auto", "iso8601" should be omitted.
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "2000-05-02", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "2000-05-02[u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "2000-05-02", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "2000-05-02[u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "2000-05-02[u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const date = new Temporal.PlainDate(2000, 5, 2, ...args);
+ const result = date.toString({ calendarName: "auto" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = auto`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-critical.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-critical.js
new file mode 100644
index 0000000000..ceb1876546
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-critical.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tostring
+description: >
+ If calendarName is "calendar", the calendar ID should be included and prefixed
+ with "!".
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "2000-05-02[!u-ca=iso8601]", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "2000-05-02[!u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "2000-05-02[!u-ca=iso8601]", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "2000-05-02[!u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "2000-05-02[!u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const date = new Temporal.PlainDate(2000, 5, 2, ...args);
+ const result = date.toString({ calendarName: "critical" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = critical`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-invalid-string.js
new file mode 100644
index 0000000000..6bdcddd835
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-invalid-string.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.protoype.tostring
+description: RangeError thrown when calendarName option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « *"string"* », « *"auto"*, *"always"*, *"never"*, *"critical"* », *"auto"*).
+ sec-temporal.plaindate.protoype.tostring step 4:
+ 4. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const invalidValues = ["ALWAYS", "sometimes", "other string", "auto\0"];
+
+for (const calendarName of invalidValues) {
+ assert.throws(
+ RangeError,
+ () => date.toString({ calendarName }),
+ `${calendarName} is an invalid value for calendarName option`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-never.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-never.js
new file mode 100644
index 0000000000..0c77b349f1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-never.js
@@ -0,0 +1,48 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tostring
+description: If calendarName is "never", the calendar ID should be omitted.
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "2000-05-02", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "2000-05-02", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "2000-05-02", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "2000-05-02", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "2000-05-02", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const date = new Temporal.PlainDate(2000, 5, 2, ...args);
+ const result = date.toString({ calendarName: "never" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = never`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-undefined.js
new file mode 100644
index 0000000000..ef4e32a99a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-undefined.js
@@ -0,0 +1,56 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.protoype.tostring
+description: Fallback value for calendarName option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « *"string"* », « *"auto"*, *"always"*, *"never"*, *"critical"* », *"auto"*).
+ sec-temporal.plaindate.protoype.tostring step 4:
+ 4. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "2000-05-02", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "2000-05-02[u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "2000-05-02", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "2000-05-02[u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "2000-05-02[u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const date = new Temporal.PlainDate(2000, 5, 2, ...args);
+ const result = date.toString({ calendarName: undefined });
+ assert.sameValue(result, expected, `default calendarName option is auto with ${description} calendar`);
+ // See options-object.js for {} and options-undefined.js for absent options arg
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-wrong-type.js
new file mode 100644
index 0000000000..2137bf50ec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-wrong-type.js
@@ -0,0 +1,49 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.protoype.tostring
+description: Type conversions for calendarName option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « *"string"* », « *"auto"*, *"always"*, *"never"*, *"critical"* », *"auto"*).
+ sec-temporal.plaindate.protoype.tostring step 4:
+ 4. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = {
+ id: "custom",
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const date = new Temporal.PlainDate(2000, 5, 2, calendar);
+
+TemporalHelpers.checkStringOptionWrongType("calendarName", "auto",
+ (calendarName) => date.toString({ calendarName }),
+ (result, descr) => assert.sameValue(result, "2000-05-02[u-ca=custom]", descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/length.js
new file mode 100644
index 0000000000..43c3eefba3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tostring
+description: Temporal.PlainDate.prototype.toString.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.toString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/name.js
new file mode 100644
index 0000000000..f18c131ab2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tostring
+description: Temporal.PlainDate.prototype.toString.name is "toString".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.toString, "name", {
+ value: "toString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/not-a-constructor.js
new file mode 100644
index 0000000000..a3f47607d2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tostring
+description: >
+ Temporal.PlainDate.prototype.toString does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.toString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.toString), false,
+ "isConstructor(Temporal.PlainDate.prototype.toString)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/options-object.js
new file mode 100644
index 0000000000..32da641983
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/options-object.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tostring
+description: Empty or a function object may be used as options
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const result1 = instance.toString({});
+assert.sameValue(
+ result1, "2000-05-02",
+ "options may be an empty plain object"
+);
+
+const result2 = instance.toString(() => {});
+assert.sameValue(
+ result2, "2000-05-02",
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/options-undefined.js
new file mode 100644
index 0000000000..5b44173c28
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/options-undefined.js
@@ -0,0 +1,48 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tostring
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const calendar = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "custom",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const date1 = new Temporal.PlainDate(2000, 5, 2);
+const date2 = new Temporal.PlainDate(2000, 5, 2, calendar);
+
+[
+ [date1, "2000-05-02"],
+ [date2, "2000-05-02[u-ca=custom]"],
+].forEach(([date, expected]) => {
+ const explicit = date.toString(undefined);
+ assert.sameValue(explicit, expected, "default calendarName option is auto");
+
+ const implicit = date.toString();
+ assert.sameValue(implicit, expected, "default calendarName option is auto");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/options-wrong-type.js
new file mode 100644
index 0000000000..5b0589dc3b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/options-wrong-type.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tostring
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.toString(value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/order-of-operations.js
new file mode 100644
index 0000000000..1872b8b5ce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/order-of-operations.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tostring
+description: Properties on an object passed to toString() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get options.calendarName",
+ "get options.calendarName.toString",
+ "call options.calendarName.toString",
+ "get this.calendar.id",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ calendarName: "auto",
+}, "options");
+
+instance.toString(options);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/prop-desc.js
new file mode 100644
index 0000000000..4575c55739
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tostring
+description: The "toString" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.toString,
+ "function",
+ "`typeof PlainDate.prototype.toString` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "toString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/year-format.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/year-format.js
new file mode 100644
index 0000000000..e6e29731c9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/year-format.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tostring
+description: Verify that the year is appropriately formatted as 4 or 6 digits
+features: [Temporal]
+---*/
+
+let instance = new Temporal.PlainDate(-100000, 12, 3);
+assert.sameValue(instance.toString(), "-100000-12-03", "large negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDate(-10000, 4, 5);
+assert.sameValue(instance.toString(), "-010000-04-05", "smallest 5-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDate(-9999, 6, 7);
+assert.sameValue(instance.toString(), "-009999-06-07", "largest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDate(-1000, 8, 9);
+assert.sameValue(instance.toString(), "-001000-08-09", "smallest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDate(-999, 10, 9);
+assert.sameValue(instance.toString(), "-000999-10-09", "largest 3-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDate(-1, 8, 7);
+assert.sameValue(instance.toString(), "-000001-08-07", "year -1 formatted as 6-digit");
+
+instance = new Temporal.PlainDate(0, 6, 5);
+assert.sameValue(instance.toString(), "0000-06-05", "year 0 formatted as 4-digit");
+
+instance = new Temporal.PlainDate(1, 4, 3);
+assert.sameValue(instance.toString(), "0001-04-03", "year 1 formatted as 4-digit");
+
+instance = new Temporal.PlainDate(999, 2, 10);
+assert.sameValue(instance.toString(), "0999-02-10", "largest 3-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainDate(1000, 1, 23);
+assert.sameValue(instance.toString(), "1000-01-23", "smallest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainDate(9999, 4, 5);
+assert.sameValue(instance.toString(), "9999-04-05", "largest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainDate(10000, 6, 7);
+assert.sameValue(instance.toString(), "+010000-06-07", "smallest 5-digit positive year formatted as 6-digit");
+
+instance = new Temporal.PlainDate(100000, 8, 9);
+assert.sameValue(instance.toString(), "+100000-08-09", "large positive year formatted as 6-digit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toStringTag/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toStringTag/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toStringTag/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toStringTag/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toStringTag/prop-desc.js
new file mode 100644
index 0000000000..d5e25e8242
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toStringTag/prop-desc.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype-@@tostringtag
+description: The @@toStringTag property of Temporal.PlainDate
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype, Symbol.toStringTag, {
+ value: "Temporal.PlainDate",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toStringTag/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toStringTag/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toStringTag/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-number.js
new file mode 100644
index 0000000000..6c45963cf3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-number.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: A number is invalid in place of an ISO string for Temporal.PlainTime
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const numbers = [
+ 1,
+ -123456.987654321,
+ 1234567,
+ 123456.9876543219,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" }),
+ `A number (${arg}) is not a valid ISO string for PlainTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..b6135a89ea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-calendar-annotation.js
@@ -0,0 +1,42 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["12:34:56.987654321[u-ca=iso8601]", "without time zone"],
+ ["12:34:56.987654321[UTC][u-ca=iso8601]", "with time zone"],
+ ["12:34:56.987654321[!u-ca=iso8601]", "with ! and no time zone"],
+ ["12:34:56.987654321[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["T12:34:56.987654321[u-ca=iso8601]", "with T and no time zone"],
+ ["T12:34:56.987654321[UTC][u-ca=iso8601]", "with T and time zone"],
+ ["T12:34:56.987654321[!u-ca=iso8601]", "with T, !, and no time zone"],
+ ["T12:34:56.987654321[UTC][!u-ca=iso8601]", "with T, !, and time zone"],
+ ["1970-01-01T12:34:56.987654321[u-ca=iso8601]", "with date and no time zone"],
+ ["1970-01-01T12:34:56.987654321[UTC][u-ca=iso8601]", "with date and time zone"],
+ ["1970-01-01T12:34:56.987654321[!u-ca=iso8601]", "with !, date, and no time zone"],
+ ["1970-01-01T12:34:56.987654321[UTC][!u-ca=iso8601]", "with !, date, and time zone"],
+ ["12:34:56.987654321[u-ca=hebrew]", "calendar annotation ignored"],
+ ["12:34:56.987654321[u-ca=unknown]", "calendar annotation ignored even if unknown calendar"],
+ ["12:34:56.987654321[!u-ca=unknown]", "calendar annotation ignored even if unknown calendar with !"],
+ ["1970-01-01T12:34:56.987654321[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" });
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 957_270_896_987_654_321n,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..7f3c36037b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "00:00[!foo=bar]",
+ "T00:00[!foo=bar]",
+ "1970-01-01T00:00[!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" }),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..6c9d395886
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-date-with-utc-offset.js
@@ -0,0 +1,52 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const validStrings = [
+ "12:34:56.987654321+00:00",
+ "12:34:56.987654321+00:00[UTC]",
+ "12:34:56.987654321+00:00[!UTC]",
+ "12:34:56.987654321-02:30[America/St_Johns]",
+ "1976-11-18T12:34:56.987654321+00:00",
+ "1976-11-18T12:34:56.987654321+00:00[UTC]",
+ "1976-11-18T12:34:56.987654321+00:00[!UTC]",
+ "1976-11-18T12:34:56.987654321-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" });
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 957_270_896_987_654_321n,
+ `"${arg}" is a valid UTC offset with time for PlainTime`
+ );
+}
+
+const invalidStrings = [
+ "2022-09-15Z",
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" }),
+ `"${arg}" UTC offset without time is not valid for PlainTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..527260a260
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-multiple-calendar.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" }),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..c72d9cc208
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-multiple-time-zone.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "00:00[UTC][UTC]",
+ "T00:00[UTC][UTC]",
+ "1970-01-01T00:00[UTC][UTC]",
+ "1970-01-01T00:00[!UTC][UTC]",
+ "1970-01-01T00:00[UTC][!UTC]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00[UTC][foo=bar][UTC]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" }),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-no-implicit-midnight.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-no-implicit-midnight.js
new file mode 100644
index 0000000000..8845c8cd45
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-no-implicit-midnight.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: RangeError thrown if a date-only string is passed in a PlainTime context
+features: [Temporal, arrow-function]
+---*/
+
+const arg = "2019-10-01";
+const instance = new Temporal.PlainDate(2000, 5, 2);
+assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" }),
+ "Date-only string throws, does not implicitly convert to midnight"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-time-designator-required-for-disambiguation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-time-designator-required-for-disambiguation.js
new file mode 100644
index 0000000000..99693c739d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-time-designator-required-for-disambiguation.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: ISO 8601 time designator "T" required in cases of ambiguity
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+TemporalHelpers.ISO.plainTimeStringsAmbiguous().forEach((string) => {
+ let arg = string;
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" }),
+ `'${arg}' is ambiguous and requires T prefix`
+ );
+ // The same string with a T prefix should not throw:
+ arg = `T${string}`;
+ instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" });
+
+ arg = ` ${string}`;
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" }),
+ `space is not accepted as a substitute for T prefix: '${arg}'`
+ );
+});
+
+// None of these should throw without a T prefix, because they are unambiguously time strings:
+TemporalHelpers.ISO.plainTimeStringsUnambiguous().forEach(
+ (arg) => instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-time-separators.js
new file mode 100644
index 0000000000..2855e5ffd9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-time-separators.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1976-11-18T12:34:56.987654321", "uppercase T"],
+ ["1976-11-18t12:34:56.987654321", "lowercase T"],
+ ["1976-11-18 12:34:56.987654321", "space between date and time"],
+ ["T12:34:56.987654321", "time-only uppercase T"],
+ ["t12:34:56.987654321", "time-only lowercase T"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" });
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 957_270_896_987_654_321n,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..03aa7e6b03
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-time-zone-annotation.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["12:34:56.987654321[Asia/Kolkata]", "named, with no offset"],
+ ["12:34:56.987654321[!Europe/Vienna]", "named, with ! and no offset"],
+ ["12:34:56.987654321[+00:00]", "numeric, with no offset"],
+ ["12:34:56.987654321[!-02:30]", "numeric, with ! and no offset"],
+ ["T12:34:56.987654321[UTC]", "named, with T and no offset"],
+ ["T12:34:56.987654321[!Africa/Abidjan]", "named, with T, !, and no offset"],
+ ["T12:34:56.987654321[+01:00]", "numeric, with T and no offset"],
+ ["T12:34:56.987654321[!-08:00]", "numeric, with T, !, and no offset"],
+ ["12:34:56.987654321+00:00[America/Sao_Paulo]", "named, with offset"],
+ ["12:34:56.987654321+00:00[!Asia/Tokyo]", "named, with ! and offset"],
+ ["12:34:56.987654321+00:00[-02:30]", "numeric, with offset"],
+ ["12:34:56.987654321+00:00[!+00:00]", "numeric, with ! and offset"],
+ ["T12:34:56.987654321+00:00[America/New_York]", "named, with T and offset"],
+ ["T12:34:56.987654321+00:00[!UTC]", "named, with T, !, and offset"],
+ ["T12:34:56.987654321+00:00[-08:00]", "numeric, with T and offset"],
+ ["T12:34:56.987654321+00:00[!+01:00]", "numeric, with T, !, and offset"],
+ ["1970-01-01T12:34:56.987654321[Africa/Lagos]", "named, with date and no offset"],
+ ["1970-01-01T12:34:56.987654321[!America/Vancouver]", "named, with date, !, and no offset"],
+ ["1970-01-01T12:34:56.987654321[+00:00]", "numeric, with date and no offset"],
+ ["1970-01-01T12:34:56.987654321[!-02:30]", "numeric, with date, !, and no offset"],
+ ["1970-01-01T12:34:56.987654321+00:00[Europe/London]", "named, with date and offset"],
+ ["1970-01-01T12:34:56.987654321+00:00[!Asia/Seoul]", "named, with date, offset, and !"],
+ ["1970-01-01T12:34:56.987654321+00:00[+01:00]", "numeric, with date and offset"],
+ ["1970-01-01T12:34:56.987654321+00:00[!-08:00]", "numeric, with date, offset, and !"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" });
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 957_270_896_987_654_321n,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..83c2cf209f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-unknown-annotation.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["12:34:56.987654321[foo=bar]", "alone"],
+ ["12:34:56.987654321[UTC][foo=bar]", "with time zone"],
+ ["12:34:56.987654321[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["T12:34:56.987654321[foo=bar]", "with T"],
+ ["T12:34:56.987654321[UTC][foo=bar]", "with T and time zone"],
+ ["T12:34:56.987654321[u-ca=iso8601][foo=bar]", "with T and calendar"],
+ ["T12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with T, time zone, and calendar"],
+ ["1970-01-01T12:34:56.987654321[foo=bar]", "with date"],
+ ["1970-01-01T12:34:56.987654321[UTC][foo=bar]", "with date and time zone"],
+ ["1970-01-01T12:34:56.987654321[u-ca=iso8601][foo=bar]", "with date and calendar"],
+ ["1970-01-01T12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with date, time zone, and calendar"],
+ ["1970-01-01T12:34:56.987654321[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" });
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 957_270_896_987_654_321n,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-with-time-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-with-time-designator.js
new file mode 100644
index 0000000000..d5fb0b8304
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-with-time-designator.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: ISO 8601 time designator "T" allowed at the start of PlainTime strings
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 1, 1);
+const validStrings = [
+ "T00:30",
+ "t00:30",
+ "T0030",
+ "t0030",
+ "T00:30:00",
+ "t00:30:00",
+ "T003000",
+ "t003000",
+ "T00:30:00.000000000",
+ "t00:30:00.000000000",
+ "T003000.000000000",
+ "t003000.000000000",
+];
+validStrings.forEach((arg) => {
+ const result = instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" });
+ assert.sameValue(result.epochNanoseconds, 946686600_000_000_000n, `T prefix is accepted: ${arg}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..fe1278b2c7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-with-utc-designator.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: RangeError thrown if a string with UTC designator is used as a PlainTime
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+ "09:00:00Z[UTC]",
+ "09:00:00Z",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" }),
+ "String with UTC designator should not be valid as a PlainTime"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-wrong-type.js
new file mode 100644
index 0000000000..e2f64f517b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-wrong-type.js
@@ -0,0 +1,42 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainTime
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" }),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainTime, "Temporal.PlainTime, object"],
+ [Temporal.PlainTime.prototype, "Temporal.PlainTime.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" }), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..f8cb95c755
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const result = instance.toZonedDateTime({ plainTime: datetime, timeZone: "UTC" });
+assert.sameValue(result.epochNanoseconds, 957286235_000_000_001n);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/basic.js
new file mode 100644
index 0000000000..1811886891
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/basic.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: Basic tests for toZonedDateTime().
+features: [Temporal]
+---*/
+
+const plainDate = Temporal.PlainDate.from("2020-01-01");
+const timeZone = Temporal.TimeZone.from("UTC");
+const plainTime = Temporal.PlainTime.from("12:00");
+
+let result = plainDate.toZonedDateTime({ timeZone, plainTime });
+assert.sameValue(result.toString(), "2020-01-01T12:00:00+00:00[UTC]", "objects passed");
+
+result = plainDate.toZonedDateTime(timeZone);
+assert.sameValue(result.toString(), "2020-01-01T00:00:00+00:00[UTC]", "time zone object argument");
+
+result = plainDate.toZonedDateTime("UTC");
+assert.sameValue(result.toString(), "2020-01-01T00:00:00+00:00[UTC]", "time zone string argument");
+
+result = plainDate.toZonedDateTime({ timeZone });
+assert.sameValue(result.toString(), "2020-01-01T00:00:00+00:00[UTC]", "time zone object property");
+
+result = plainDate.toZonedDateTime({ timeZone: "UTC", plainTime });
+assert.sameValue(result.toString(), "2020-01-01T12:00:00+00:00[UTC]", "time zone string property");
+
+result = plainDate.toZonedDateTime({ timeZone, plainTime: "12:00" });
+assert.sameValue(result.toString(), "2020-01-01T12:00:00+00:00[UTC]", "time string property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/branding.js
new file mode 100644
index 0000000000..9eddebf6d0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toZonedDateTime = Temporal.PlainDate.prototype.toZonedDateTime;
+
+assert.sameValue(typeof toZonedDateTime, "function");
+
+const args = [new Temporal.TimeZone("UTC")];
+
+assert.throws(TypeError, () => toZonedDateTime.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => toZonedDateTime.apply(null, args), "null");
+assert.throws(TypeError, () => toZonedDateTime.apply(true, args), "true");
+assert.throws(TypeError, () => toZonedDateTime.apply("", args), "empty string");
+assert.throws(TypeError, () => toZonedDateTime.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => toZonedDateTime.apply(1, args), "1");
+assert.throws(TypeError, () => toZonedDateTime.apply({}, args), "plain object");
+assert.throws(TypeError, () => toZonedDateTime.apply(Temporal.PlainDate, args), "Temporal.PlainDate");
+assert.throws(TypeError, () => toZonedDateTime.apply(Temporal.PlainDate.prototype, args), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/builtin.js
new file mode 100644
index 0000000000..c31498e3c5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: >
+ Tests that Temporal.PlainDate.prototype.toZonedDateTime
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.toZonedDateTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.toZonedDateTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.toZonedDateTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.toZonedDateTime.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/calendar.js
new file mode 100644
index 0000000000..485ee36087
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/calendar.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: Calendar of the receiver is used
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+const timeCalendar = { toString() { return "iso8601"; } };
+const plainDate = new Temporal.PlainDate(2000, 5, 2, calendar);
+const result = plainDate.toZonedDateTime({
+ timeZone: "UTC",
+ plainTime: { hour: 12, minute: 30, calendar: timeCalendar },
+});
+assert.sameValue(result.epochNanoseconds, 957270600_000_000_000n);
+assert.sameValue(result.timeZoneId, "UTC");
+assert.sameValue(result.getCalendar(), calendar);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/getpossibleinstantsfor-called-with-iso8601-calendar.js
new file mode 100644
index 0000000000..9dcceb14a9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/getpossibleinstantsfor-called-with-iso8601-calendar.js
@@ -0,0 +1,58 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: >
+ Time zone's getPossibleInstantsFor is called with a PlainDateTime with the
+ built-in ISO 8601 calendar
+features: [Temporal]
+info: |
+ DisambiguatePossibleInstants:
+ 2. Let _n_ be _possibleInstants_'s length.
+ ...
+ 5. Assert: _n_ = 0.
+ ...
+ 19. If _disambiguation_ is *"earlier"*, then
+ ...
+ c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_).
+ ...
+ 20. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ ...
+ 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_).
+---*/
+
+class SkippedDateTime extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ this.calls = 0;
+ }
+
+ getPossibleInstantsFor(dateTime) {
+ // Calls occur in pairs. For the first one return no possible instants so
+ // that DisambiguatePossibleInstants will call it again
+ if (this.calls++ % 2 == 0) {
+ return [];
+ }
+
+ assert.sameValue(
+ dateTime.getISOFields().calendar,
+ "iso8601",
+ "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar"
+ );
+ return super.getPossibleInstantsFor(dateTime);
+ }
+}
+
+const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601");
+const timeZone = new SkippedDateTime();
+
+const instance = new Temporal.PlainDate(2000, 5, 2, nonBuiltinISOCalendar);
+instance.toZonedDateTime(timeZone);
+
+assert.sameValue(timeZone.calls, 2, "getPossibleInstantsFor should have been called 2 times");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/leap-second.js
new file mode 100644
index 0000000000..2560495911
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/leap-second.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: Leap second is a valid ISO string for PlainTime
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" });
+assert.sameValue(
+ result1.epochNanoseconds,
+ 957311999_000_000_000n,
+ "leap second is a valid ISO string for PlainTime"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" });
+assert.sameValue(
+ result2.epochNanoseconds,
+ 957311999_000_000_000n,
+ "second: 60 is ignored in property bag for PlainTime"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/length.js
new file mode 100644
index 0000000000..405ef56df9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: Temporal.PlainDate.prototype.toZonedDateTime.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.toZonedDateTime, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/name.js
new file mode 100644
index 0000000000..15818816d2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: Temporal.PlainDate.prototype.toZonedDateTime.name is "toZonedDateTime".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.toZonedDateTime, "name", {
+ value: "toZonedDateTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/not-a-constructor.js
new file mode 100644
index 0000000000..80ad27d974
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: >
+ Temporal.PlainDate.prototype.toZonedDateTime does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.toZonedDateTime();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.toZonedDateTime), false,
+ "isConstructor(Temporal.PlainDate.prototype.toZonedDateTime)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/order-of-operations.js
new file mode 100644
index 0000000000..b84a4c5fa4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/order-of-operations.js
@@ -0,0 +1,98 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: User code calls happen in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "get item.timeZone",
+ "has item.timeZone.getOffsetNanosecondsFor",
+ "has item.timeZone.getPossibleInstantsFor",
+ "has item.timeZone.id",
+ "get item.plainTime",
+ // ToTemporalTime
+ "get item.plainTime.hour",
+ "get item.plainTime.hour.valueOf",
+ "call item.plainTime.hour.valueOf",
+ "get item.plainTime.microsecond",
+ "get item.plainTime.microsecond.valueOf",
+ "call item.plainTime.microsecond.valueOf",
+ "get item.plainTime.millisecond",
+ "get item.plainTime.millisecond.valueOf",
+ "call item.plainTime.millisecond.valueOf",
+ "get item.plainTime.minute",
+ "get item.plainTime.minute.valueOf",
+ "call item.plainTime.minute.valueOf",
+ "get item.plainTime.nanosecond",
+ "get item.plainTime.nanosecond.valueOf",
+ "call item.plainTime.nanosecond.valueOf",
+ "get item.plainTime.second",
+ "get item.plainTime.second.valueOf",
+ "call item.plainTime.second.valueOf",
+ // lookup in PlainDate.p.toZonedDateTime
+ "get item.timeZone.getOffsetNanosecondsFor",
+ "get item.timeZone.getPossibleInstantsFor",
+ // GetInstantFor
+ "call item.timeZone.getPossibleInstantsFor",
+];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainDate(2000, 1, 1, calendar);
+const springForwardInstance = new Temporal.PlainDate(2000, 4, 2, calendar);
+const fallBackInstance = new Temporal.PlainDate(2000, 10, 29, calendar);
+actual.splice(0); // clear calendar calls that happened in constructors
+
+const plainTime = TemporalHelpers.propertyBagObserver(actual, {
+ hour: 2,
+ minute: 30,
+ second: 0,
+ millisecond: 0,
+ microsecond: 0,
+ nanosecond: 0,
+}, "item.plainTime");
+const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "item.timeZone", {
+ getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor,
+ getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor,
+});
+const item = TemporalHelpers.propertyBagObserver(actual, {
+ plainTime,
+ timeZone,
+}, "item");
+
+instance.toZonedDateTime(item);
+assert.compareArray(actual, expected, "order of operations at normal wall-clock time");
+actual.splice(0); // clear
+
+const plainTime130 = TemporalHelpers.propertyBagObserver(actual, {
+ hour: 1,
+ minute: 30,
+ second: 0,
+ millisecond: 0,
+ microsecond: 0,
+ nanosecond: 0,
+}, "item.plainTime");
+const item130 = TemporalHelpers.propertyBagObserver(actual, {
+ plainTime: plainTime130,
+ timeZone,
+}, "item");
+
+fallBackInstance.toZonedDateTime(item130);
+assert.compareArray(actual, expected, "order of operations at repeated wall-clock time");
+actual.splice(0); // clear
+
+springForwardInstance.toZonedDateTime(item);
+assert.compareArray(actual, expected.concat([
+ "call item.timeZone.getOffsetNanosecondsFor",
+ "call item.timeZone.getOffsetNanosecondsFor",
+ "call item.timeZone.getPossibleInstantsFor",
+]), "order of operations at skipped wall-clock time");
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 0000000000..e631b28963
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,49 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: Negative time fields are balanced upwards in a ZonedDateTime given as plainTime
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-temporal-totemporaltime step 3.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ ii. 1. Set _plainDateTime_ to ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
+ sec-temporal.plaindate.prototype.tozoneddatetime step 6.a:
+ a. Set _temporalTime_ to ? ToTemporalTime(_temporalTime_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// This code path is encountered if the time zone offset is negative and its
+// absolute value in nanoseconds is greater than the nanosecond field of the
+// exact time's epoch parts
+const tz = TemporalHelpers.specificOffsetTimeZone(-2);
+const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
+
+const otherTimeZone = new Temporal.TimeZone("UTC"); // should not be used to convert datetime to PlainTime
+const date = new Temporal.PlainDate(2000, 5, 2);
+const zdt = date.toZonedDateTime({ timeZone: otherTimeZone, plainTime: datetime });
+
+assert.sameValue(zdt.microsecond, 0);
+assert.sameValue(zdt.nanosecond, 999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..576fc4b23a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.toZonedDateTime({ plainTime: datetime, timeZone: "UTC" }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..7adf2dd547
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => date.toZonedDateTime({ plainTime: datetime, timeZone: "UTC" }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..d29f3afa1f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.toZonedDateTime({ plainTime: datetime, timeZone: "UTC" }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..f2ad7aa761
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => date.toZonedDateTime({ plainTime: datetime, timeZone: "UTC" }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-propertybag-no-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-propertybag-no-time-units.js
new file mode 100644
index 0000000000..f29691c8c9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-propertybag-no-time-units.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: Missing time units in property bag default to 0
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 1, 1);
+
+const props = {};
+assert.throws(TypeError, () => instance.toZonedDateTime({ plainTime: props, timeZone: "UTC" }), "TypeError if no properties are present");
+
+props.minute = 30;
+const result = instance.toZonedDateTime({ plainTime: props, timeZone: "UTC" });
+assert.sameValue(result.epochNanoseconds, 946686600_000_000_000n, "missing time units default to 0");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/prop-desc.js
new file mode 100644
index 0000000000..04db137fc2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: The "toZonedDateTime" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.toZonedDateTime,
+ "function",
+ "`typeof PlainDate.prototype.toZonedDateTime` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "toZonedDateTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-case-insensitive.js
new file mode 100644
index 0000000000..9e66040e43
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-case-insensitive.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: Time zone names are case insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const timeZone = 'uTc';
+const result = instance.toZonedDateTime(timeZone);
+assert.sameValue(result.timeZoneId, 'UTC', `Time zone created from string "${timeZone}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..1b664b2991
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const plainTime = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ assert.throws(RangeError, () => date.toZonedDateTime({ plainTime, timeZone }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..d4096890d3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const plainTime = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => date.toZonedDateTime({ plainTime, timeZone }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..8d96870354
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const plainTime = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ assert.throws(RangeError, () => date.toZonedDateTime({ plainTime, timeZone }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..d59f7149ae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const plainTime = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ assert.throws(TypeError, () => date.toZonedDateTime({ plainTime, timeZone }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 0000000000..05331a4606
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.plaindate.prototype.tozoneddatetime step 7:
+ 7. Let _instant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _temporalDateTime_, *"compatible"*).
+ sec-temporal-builtintimezonegetinstantfor step 1:
+ 1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-builtintimezonegetinstantfor step 14:
+ 14. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ sec-temporal-builtintimezonegetinstantfor step 16:
+ 16. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _later_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected1 = [
+ "2000-05-02T00:00:00",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ date.toZonedDateTime(timeZone);
+}, expected1);
+
+// Same, but test the other path where the time doesn't exist and
+// GetPossibleInstantsFor is called again on a later time
+
+const expected2 = [
+ "2030-01-01T00:30:00",
+ "2030-01-01T01:30:00",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const date = new Temporal.PlainDate(2030, 1, 1);
+ date.toZonedDateTime({ plainTime: new Temporal.PlainTime(0, 30), timeZone });
+}, expected2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getpossibleinstantsfor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getpossibleinstantsfor.js
new file mode 100644
index 0000000000..1a1d2f8494
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getpossibleinstantsfor.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: Calendar of the receiver is used
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+class CustomTimeZone extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ }
+ getPossibleInstantsFor(plainDateTime) {
+ assert.sameValue(plainDateTime.getCalendar(), calendar);
+ return [new Temporal.Instant(987654321_000_000_000n)];
+ }
+}
+const timeZone = new CustomTimeZone();
+const plainDate = new Temporal.PlainDate(2000, 5, 2, calendar);
+const result = plainDate.toZonedDateTime({
+ timeZone,
+ plainTime: { hour: 12, minute: 30 },
+});
+assert.sameValue(result.epochNanoseconds, 987654321_000_000_000n);
+assert.sameValue(result.getTimeZone(), timeZone);
+assert.sameValue(result.getCalendar(), calendar);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-datetime.js
new file mode 100644
index 0000000000..24c2c66267
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-datetime.js
@@ -0,0 +1,65 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => instance.toZonedDateTime(timeZone), "bare date-time string is not a time zone");
+
+[
+ "2021-08-19T17:30-07:00:01",
+ "2021-08-19T17:30-07:00:00",
+ "2021-08-19T17:30-07:00:00.1",
+ "2021-08-19T17:30-07:00:00.0",
+ "2021-08-19T17:30-07:00:00.01",
+ "2021-08-19T17:30-07:00:00.00",
+ "2021-08-19T17:30-07:00:00.001",
+ "2021-08-19T17:30-07:00:00.000",
+ "2021-08-19T17:30-07:00:00.0001",
+ "2021-08-19T17:30-07:00:00.0000",
+ "2021-08-19T17:30-07:00:00.00001",
+ "2021-08-19T17:30-07:00:00.00000",
+ "2021-08-19T17:30-07:00:00.000001",
+ "2021-08-19T17:30-07:00:00.000000",
+ "2021-08-19T17:30-07:00:00.0000001",
+ "2021-08-19T17:30-07:00:00.0000000",
+ "2021-08-19T17:30-07:00:00.00000001",
+ "2021-08-19T17:30-07:00:00.00000000",
+ "2021-08-19T17:30-07:00:00.000000001",
+ "2021-08-19T17:30-07:00:00.000000000",
+].forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime(timeZone),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone`
+ );
+});
+
+timeZone = "2021-08-19T17:30Z";
+const result1 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result1.timeZoneId, "UTC", "date-time + Z is UTC time zone");
+
+timeZone = "2021-08-19T17:30-07:00";
+const result2 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result2.timeZoneId, "-07:00", "date-time + offset is the offset time zone");
+
+timeZone = "2021-08-19T17:30[UTC]";
+const result3 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result3.timeZoneId, "UTC", "date-time + IANA annotation is the IANA time zone");
+
+timeZone = "2021-08-19T17:30Z[UTC]";
+const result4 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result4.timeZoneId, "UTC", "date-time + Z + IANA annotation is the IANA time zone");
+
+timeZone = "2021-08-19T17:30-07:00[UTC]";
+const result5 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result5.timeZoneId, "UTC", "date-time + offset + IANA annotation is the IANA time zone");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-leap-second.js
new file mode 100644
index 0000000000..ee9b23f844
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-leap-second.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: Leap second is a valid ISO string for TimeZone
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
+
+const result = instance.toZonedDateTime(timeZone);
+assert.sameValue(result.timeZoneId, "UTC", "leap second is a valid ISO string for TimeZone");
+
+timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
+assert.throws(RangeError, () => instance.toZonedDateTime(timeZone), "leap second in time zone name not valid");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-multiple-offsets.js
new file mode 100644
index 0000000000..144bf2d184
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-multiple-offsets.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
+
+const result = instance.toZonedDateTime(timeZone);
+assert.sameValue(result.timeZoneId, "+01:46", "Time zone string determined from bracket name");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-year-zero.js
new file mode 100644
index 0000000000..f17dae299b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-year-zero.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime(timeZone),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string.js
new file mode 100644
index 0000000000..80efaabacc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: Time zone IDs are valid input for a time zone
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+["UTC", "+01:30"].forEach((timeZone) => {
+ const result = instance.toZonedDateTime(timeZone);
+ assert.sameValue(result.getISOFields().timeZone, timeZone, `time zone slot should store string "${timeZone}"`);
+});
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-wrong-type.js
new file mode 100644
index 0000000000..88b2ae55d4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-wrong-type.js
@@ -0,0 +1,42 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [timeZone, description] of primitiveTests) {
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => instance.toZonedDateTime(timeZone),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "object not implementing time zone protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.toZonedDateTime(timeZone), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/year-zero.js
new file mode 100644
index 0000000000..99b5545515
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/year-zero.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-12-07T03:24:30",
+ "-000000-12-07T03:24:30+01:00",
+ "-000000-12-07T03:24:30+00:00[UTC]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" }),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..65a50eeeb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.until(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..81df965c93
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-calendar-datefromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.until(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..11d0874d9f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-constructor-in-calendar-fields.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+assert.throws(RangeError, () => instance.until(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..ef00260ae3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.PlainDate(2000, 5, 2);
+
+ assert.throws(RangeError, () => instance.until(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-leap-second.js
new file mode 100644
index 0000000000..e62b69de21
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-leap-second.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Leap second is a valid ISO string for PlainDate
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2016, 12, 31);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.until(arg);
+TemporalHelpers.assertDuration(
+ result1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for PlainDate"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.until(arg);
+TemporalHelpers.assertDuration(
+ result2,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "second: 60 is ignored in property bag for PlainDate"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-number.js
new file mode 100644
index 0000000000..c181972581
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-number.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.until(arg),
+ 'Numbers cannot be used in place of an ISO string for PlainDate'
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-plaindatetime.js
new file mode 100644
index 0000000000..8dfa1b88ad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-plaindatetime.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.until
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainDate by reading internal slots
+info: |
+ sec-temporal.plaindate.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalDate(_other_).
+ sec-temporal-totemporaldate step 2.b:
+ b. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ i. Return ! CreateTemporalDate(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const result = date.until(datetime);
+ assert.sameValue(result.total({ unit: "nanoseconds" }), 0, "time part dropped");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..c3a029272a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: The calendar name is case-insensitive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.until(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..a05da04f05
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Leap second is a valid ISO string for a calendar in a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.until(arg);
+TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..c46e6ff2ec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-number.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.until(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..2dddc518d7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-string.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.until(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..65304590aa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.until(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.until(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..abe100487e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-year-zero.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..01c0f901aa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-proto-in-calendar-fields.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+assert.throws(RangeError, () => instance.until(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..a19cb0caff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-calendar-annotation.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..ac10df4a67
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!foo=bar]",
+ "1970-01-01T00:00[!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..06692bd6b3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-date-with-utc-offset.js
@@ -0,0 +1,49 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.until(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+const invalidStrings = [
+ "2022-09-15Z",
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-invalid.js
new file mode 100644
index 0000000000..47c10e5c58
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-invalid.js
@@ -0,0 +1,64 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `"${arg}" should not be a valid ISO string for a PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..db7f9127f1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-multiple-calendar.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..1ec82cadc5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-multiple-time-zone.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[UTC][UTC]",
+ "1970-01-01T00:00[UTC][UTC]",
+ "1970-01-01T00:00[!UTC][UTC]",
+ "1970-01-01T00:00[UTC][!UTC]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00[UTC][foo=bar][UTC]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-time-separators.js
new file mode 100644
index 0000000000..e9c944cc64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-time-separators.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..f8cdb4306e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-time-zone-annotation.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[Asia/Kolkata]", "named, with no time"],
+ ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"],
+ ["2000-05-02[+00:00]", "numeric, with no time"],
+ ["2000-05-02[!-02:30]", "numeric, with ! and no time"],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..c8b5af5c6f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-unknown-annotation.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..95f193aed7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-with-utc-designator.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ "String with UTC designator should not be valid as a PlainDate"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-wrong-type.js
new file mode 100644
index 0000000000..d6886c8c7d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-wrong-type.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => instance.until(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.until(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-convert.js
new file mode 100644
index 0000000000..807b83051b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+assert.throws(Test262Error, () => instance.until(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..7777ce9c05
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-slots.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+const instance = new Temporal.PlainDate(1976, 11, 18);
+instance.until(arg);
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..cc57645903
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.until(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..12ee440503
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => date.until(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..31bf9a778f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.until(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..5bdc156026
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => date.until(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/basic.js
new file mode 100644
index 0000000000..d2166e9e3e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/basic.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Basic tests for until().
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(1969, 7, 24);
+const plainDate2 = Temporal.PlainDate.from({ year: 1969, month: 10, day: 5 });
+TemporalHelpers.assertDuration(plainDate.until(plainDate2), 0, 0, 0, /* days = */ 73, 0, 0, 0, 0, 0, 0, "same year");
+
+const earlier = new Temporal.PlainDate(1969, 7, 24);
+const later = new Temporal.PlainDate(1996, 3, 3);
+TemporalHelpers.assertDuration(earlier.until(later), 0, 0, 0, /* days = */ 9719, 0, 0, 0, 0, 0, 0, "different year");
+
+TemporalHelpers.assertDuration(plainDate.until({ year: 2019, month: 7, day: 24 }), 0, 0, 0, /* days = */ 18262, 0, 0, 0, 0, 0, 0, "option bag");
+TemporalHelpers.assertDuration(plainDate.until("2019-07-24"), 0, 0, 0, /* days = */ 18262, 0, 0, 0, 0, 0, 0, "string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/branding.js
new file mode 100644
index 0000000000..2625e235f3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const until = Temporal.PlainDate.prototype.until;
+
+assert.sameValue(typeof until, "function");
+
+const args = [new Temporal.PlainDate(2022, 6, 22)];
+
+assert.throws(TypeError, () => until.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => until.apply(null, args), "null");
+assert.throws(TypeError, () => until.apply(true, args), "true");
+assert.throws(TypeError, () => until.apply("", args), "empty string");
+assert.throws(TypeError, () => until.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => until.apply(1, args), "1");
+assert.throws(TypeError, () => until.apply({}, args), "plain object");
+assert.throws(TypeError, () => until.apply(Temporal.PlainDate, args), "Temporal.PlainDate");
+assert.throws(TypeError, () => until.apply(Temporal.PlainDate.prototype, args), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..400f425860
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateUntilOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateUntil");
+Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateUntil should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.until(new Temporal.PlainDate(2001, 6, 13));
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", dateUntilOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/builtin.js
new file mode 100644
index 0000000000..d65f2e5af7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: >
+ Tests that Temporal.PlainDate.prototype.until
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.until),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.until),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.until),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.until.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-dateadd-called-with-plaindate-instance.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-dateadd-called-with-plaindate-instance.js
new file mode 100644
index 0000000000..6b72eabd28
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-dateadd-called-with-plaindate-instance.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: >
+ relativeTo parameters that are not ZonedDateTime or undefined, are always
+ converted to PlainDate for observable calendar calls
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddPlainDateInstance();
+const instance = new Temporal.PlainDate(1970, 1, 1, calendar);
+calendar.specificPlainDate = instance;
+instance.until(new Temporal.PlainDate(2000, 5, 2, calendar), { smallestUnit: "month" });
+assert(calendar.dateAddCallCount > 0, "assertions in calendar.dateAdd() should have been tested");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..c4455c2f1e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-datefromfields-called-with-options-undefined.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+instance.until({ year: 2000, month: 5, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js
new file mode 100644
index 0000000000..2945cae16d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: >
+ Calendar.dateUntil method is called with a null-prototype object as the
+ options value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckOptionsPrototypePollution();
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+const argument = new Temporal.PlainDate(2022, 6, 14, calendar);
+instance.until(argument, { largestUnit: "months" });
+assert.sameValue(calendar.dateUntilCallCount, 1, "dateUntil should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 0000000000..7a7f9d92f2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.plaindate.prototype.until steps 12–13:
+ 13. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _largestUnit_).
+ 14. Let _result_ be ? CalendarDateUntil(_temporalDate_.[[Calendar]], _temporalDate_, _other_, _untilOptions_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const earlier = new Temporal.PlainDate(2000, 5, 2, calendar);
+ const later = new Temporal.PlainDate(2001, 6, 3, calendar);
+ earlier.until(later, { largestUnit });
+ },
+ {
+ years: ["year"],
+ months: ["month"],
+ weeks: ["week"],
+ days: []
+ }
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-fields-iterable.js
new file mode 100644
index 0000000000..b77217dd0c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-fields-iterable.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindate.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalDate(_other_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const date = new Temporal.PlainDate(2000, 5, 2, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+date.until({ year: 2005, month: 6, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-id-match.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-id-match.js
new file mode 100644
index 0000000000..ddff8dae6c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-id-match.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Calculation is performed if calendars' toString results match
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class Calendar1 extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ toString() {
+ return "A";
+ }
+}
+class Calendar2 extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ toString() {
+ return "A";
+ }
+}
+
+const plainDate1 = new Temporal.PlainDate(2000, 1, 1, new Calendar1());
+const plainDate2 = new Temporal.PlainDate(2000, 1, 2, new Calendar2());
+TemporalHelpers.assertDuration(plainDate1.until(plainDate2), 0, 0, 0, /* days = */ 1, 0, 0, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-invalid-return.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-invalid-return.js
new file mode 100644
index 0000000000..632e9d69e3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-invalid-return.js
@@ -0,0 +1,42 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Throw when the returned value from the calendar's dateUntil method is not a Duration.
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor(value) {
+ super("iso8601");
+ this.value = value;
+ }
+ dateUntil() {
+ return this.value;
+ }
+}
+
+const tests = [
+ [undefined],
+ [null, "null"],
+ [true],
+ ["2000-05"],
+ [Symbol()],
+ [200005],
+ [200005n],
+ [{}, "plain object"],
+ [() => {}, "lambda"],
+ [Temporal.Duration, "Temporal.Duration"],
+ [Temporal.Duration.prototype, "Temporal.Duration.prototype"],
+];
+for (const [test, description = typeof test] of tests) {
+ const plainDate = new Temporal.PlainDate(2000, 5, 2, new CustomCalendar(test));
+ assert.throws(
+ TypeError, () => plainDate.until("2022-06-20", { largestUnit: "years" }),
+ `Expected error with ${description}`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-mismatch.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-mismatch.js
new file mode 100644
index 0000000000..65be788ce3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-mismatch.js
@@ -0,0 +1,62 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: RangeError thrown if calendars' id properties do not match
+features: [Temporal]
+---*/
+
+const calendar1 = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "A",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const calendar2 = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "B",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const plainDate1 = new Temporal.PlainDate(2000, 1, 1, calendar1);
+const plainDate2 = new Temporal.PlainDate(2000, 1, 1, calendar2);
+assert.throws(RangeError, () => plainDate1.until(plainDate2));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-temporal-object.js
new file mode 100644
index 0000000000..4f03a0b94d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindate.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalDate(_other_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const date = new Temporal.PlainDate(2000, 5, 2, temporalObject);
+ date.until({ year: 2005, month: 6, day: 2, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/custom.js
new file mode 100644
index 0000000000..c2d9d2cb82
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/custom.js
@@ -0,0 +1,35 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Basic tests with custom calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const result = new Temporal.Duration(1, 3, 5, 7, 9);
+const options = { largestUnit: "years" };
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateUntil(...args) {
+ ++calls;
+ assert.sameValue(args.length, 3, "Three arguments");
+ assert.sameValue(args[0], plainDate, "First argument");
+ assert.sameValue(args[1], other, "Second argument");
+ assert.sameValue(args[2].largestUnit, "year", "Third argument: largestUnit");
+ return result;
+ }
+}
+const calendar = new CustomCalendar();
+const plainDate = new Temporal.PlainDate(1976, 11, 18, calendar);
+const other = new Temporal.PlainDate(2022, 6, 20, calendar);
+TemporalHelpers.assertDuration(plainDate.until(other, options),
+ 1, 3, 5, 7, 0, 0, 0, 0, 0, 0, "result");
+assert.sameValue(calls, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/days-in-month.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/days-in-month.js
new file mode 100644
index 0000000000..72ff38ca21
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/days-in-month.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: until() should take length of month into account.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate1 = Temporal.PlainDate.from("2019-01-01");
+const plainDate2 = Temporal.PlainDate.from("2019-02-01");
+const plainDate3 = Temporal.PlainDate.from("2019-03-01");
+TemporalHelpers.assertDuration(plainDate1.until(plainDate2), 0, 0, 0, /* days = */ 31, 0, 0, 0, 0, 0, 0, "January 2019");
+TemporalHelpers.assertDuration(plainDate2.until(plainDate3), 0, 0, 0, /* days = */ 28, 0, 0, 0, 0, 0, 0, "February 2019");
+
+const plainDate4 = Temporal.PlainDate.from("2020-02-01");
+const plainDate5 = Temporal.PlainDate.from("2020-03-01");
+TemporalHelpers.assertDuration(plainDate4.until(plainDate5), 0, 0, 0, /* days = */ 29, 0, 0, 0, 0, 0, 0, "February 2020");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/days-in-year.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/days-in-year.js
new file mode 100644
index 0000000000..6ca8c59327
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/days-in-year.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: until() should take length of year into account.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate1 = Temporal.PlainDate.from("2019-01-01");
+const plainDate2 = Temporal.PlainDate.from("2020-01-01");
+const plainDate3 = Temporal.PlainDate.from("2021-01-01");
+TemporalHelpers.assertDuration(plainDate1.until(plainDate2), 0, 0, 0, /* days = */ 365, 0, 0, 0, 0, 0, 0, "From January 2019");
+TemporalHelpers.assertDuration(plainDate2.until(plainDate3), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "From January 2020");
+
+const plainDate4 = Temporal.PlainDate.from("2019-06-01");
+const plainDate5 = Temporal.PlainDate.from("2020-06-01");
+const plainDate6 = Temporal.PlainDate.from("2021-06-01");
+TemporalHelpers.assertDuration(plainDate4.until(plainDate5), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "From June 2019");
+TemporalHelpers.assertDuration(plainDate5.until(plainDate6), 0, 0, 0, /* days = */ 365, 0, 0, 0, 0, 0, 0, "From June 2020");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..6bd0c01f29
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/infinity-throws-rangeerror.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindate.prototype.until
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.until({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.until({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-default.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-default.js
new file mode 100644
index 0000000000..907df1f977
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-default.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Default value for largestUnit option is days
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const feb20 = Temporal.PlainDate.from("2020-02-01");
+const feb21 = Temporal.PlainDate.from("2021-02-01");
+TemporalHelpers.assertDuration(feb20.until(feb21), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "no options");
+TemporalHelpers.assertDuration(feb20.until(feb21, undefined), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "undefined options");
+TemporalHelpers.assertDuration(feb20.until(feb21, {}), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "no largestUnit");
+TemporalHelpers.assertDuration(feb20.until(feb21, { largestUnit: undefined }), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "undefined largestUnit");
+TemporalHelpers.assertDuration(feb20.until(feb21, { largestUnit: "days" }), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "days");
+TemporalHelpers.assertDuration(feb20.until(feb21, { largestUnit: "auto" }), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "auto");
+TemporalHelpers.assertDuration(feb20.until(feb21, () => {}), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "no largestUnit (function)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-higher-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-higher-units.js
new file mode 100644
index 0000000000..a8f8812d9d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-higher-units.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Tests calculations with higher largestUnit than the default of 'days'
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(1969, 7, 24);
+const later = Temporal.PlainDate.from({ year: 2019, month: 7, day: 24 });
+const duration = date.until(later, { largestUnit: "years" });
+TemporalHelpers.assertDuration(duration, /* years = */ 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, "crossing epoch");
+
+const feb20 = Temporal.PlainDate.from("2020-02-01");
+const feb21 = Temporal.PlainDate.from("2021-02-01");
+TemporalHelpers.assertDuration(feb20.until(feb21, { largestUnit: "years" }), /* years = */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "start of February, years");
+TemporalHelpers.assertDuration(feb20.until(feb21, { largestUnit: "months" }), 0, /* months = */ 12, 0, 0, 0, 0, 0, 0, 0, 0, "start of February, months");
+TemporalHelpers.assertDuration(feb20.until(feb21, { largestUnit: "weeks" }), 0, 0, /* weeks = */ 52, /* days = */ 2, 0, 0, 0, 0, 0, 0, "start of February, weeks");
+
+const lastFeb20 = Temporal.PlainDate.from("2020-02-29");
+const lastFeb21 = Temporal.PlainDate.from("2021-02-28");
+TemporalHelpers.assertDuration(lastFeb20.until(lastFeb21, { largestUnit: "years" }), /* years = */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "end of February, years");
+TemporalHelpers.assertDuration(lastFeb20.until(lastFeb21, { largestUnit: "months" }), 0, /* months = */ 12, 0, 0, 0, 0, 0, 0, 0, 0, "end of February, months");
+TemporalHelpers.assertDuration(lastFeb20.until(lastFeb21, { largestUnit: "weeks" }), 0, 0, /* weeks = */ 52, /* days = */ 1, 0, 0, 0, 0, 0, 0, "end of February, weeks");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-invalid-string.js
new file mode 100644
index 0000000000..145b8d3024
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-invalid-string.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+const badValues = [
+ "era",
+ "eraYear",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+ "month\0",
+ "YEAR",
+ "eras",
+ "eraYears",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+ "months\0",
+ "YEARS",
+ "other string"
+];
+for (const largestUnit of badValues) {
+ assert.throws(RangeError, () => earlier.until(later, { largestUnit }),
+ `"${largestUnit}" is not a valid value for largestUnit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-plurals-accepted.js
new file mode 100644
index 0000000000..84769e1877
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-plurals-accepted.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 12);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => earlier.until(later, { largestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-smallestunit-mismatch.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-smallestunit-mismatch.js
new file mode 100644
index 0000000000..91ba76b412
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-smallestunit-mismatch.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: RangeError thrown when smallestUnit is larger than largestUnit
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+const units = ["years", "months", "weeks", "days"];
+for (let largestIdx = 1; largestIdx < units.length; largestIdx++) {
+ for (let smallestIdx = 0; smallestIdx < largestIdx; smallestIdx++) {
+ const largestUnit = units[largestIdx];
+ const smallestUnit = units[smallestIdx];
+ assert.throws(RangeError, () => earlier.until(later, { largestUnit, smallestUnit }));
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-undefined.js
new file mode 100644
index 0000000000..0b24449663
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-undefined.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Fallback value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+
+const explicit = earlier.until(later, { largestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 397, 0, 0, 0, 0, 0, 0, "default largestUnit is day");
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 397, 0, 0, 0, 0, 0, 0, "default largestUnit is day");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-wrong-type.js
new file mode 100644
index 0000000000..42d56c641e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-wrong-type.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "year",
+ (largestUnit) => earlier.until(later, { largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/length.js
new file mode 100644
index 0000000000..31c204731d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Temporal.PlainDate.prototype.until.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.until, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/name.js
new file mode 100644
index 0000000000..99b7ae2be1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Temporal.PlainDate.prototype.until.name is "until".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.until, "name", {
+ value: "until",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/not-a-constructor.js
new file mode 100644
index 0000000000..2d4a6df0d0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: >
+ Temporal.PlainDate.prototype.until does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.until();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.until), false,
+ "isConstructor(Temporal.PlainDate.prototype.until)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/options-object.js
new file mode 100644
index 0000000000..978d5189f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/options-object.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const result1 = instance.until(new Temporal.PlainDate(1976, 11, 18), {});
+TemporalHelpers.assertDuration(
+ result1, 0, 0, 0, -8566, 0, 0, 0, 0, 0, 0,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.until(new Temporal.PlainDate(1976, 11, 18), () => {});
+TemporalHelpers.assertDuration(
+ result2, 0, 0, 0, -8566, 0, 0, 0, 0, 0, 0,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/options-wrong-type.js
new file mode 100644
index 0000000000..dd67a3bd6b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/options-wrong-type.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.until(new Temporal.PlainDate(1976, 11, 18), value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/order-of-operations.js
new file mode 100644
index 0000000000..427670784a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/order-of-operations.js
@@ -0,0 +1,214 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Properties on objects passed to until() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalDate
+ "get other.calendar",
+ "has other.calendar.dateAdd",
+ "has other.calendar.dateFromFields",
+ "has other.calendar.dateUntil",
+ "has other.calendar.day",
+ "has other.calendar.dayOfWeek",
+ "has other.calendar.dayOfYear",
+ "has other.calendar.daysInMonth",
+ "has other.calendar.daysInWeek",
+ "has other.calendar.daysInYear",
+ "has other.calendar.fields",
+ "has other.calendar.id",
+ "has other.calendar.inLeapYear",
+ "has other.calendar.mergeFields",
+ "has other.calendar.month",
+ "has other.calendar.monthCode",
+ "has other.calendar.monthDayFromFields",
+ "has other.calendar.monthsInYear",
+ "has other.calendar.weekOfYear",
+ "has other.calendar.year",
+ "has other.calendar.yearMonthFromFields",
+ "has other.calendar.yearOfWeek",
+ "get other.calendar.dateFromFields",
+ "get other.calendar.fields",
+ "call other.calendar.fields",
+ "get other.day",
+ "get other.day.valueOf",
+ "call other.day.valueOf",
+ "get other.month",
+ "get other.month.valueOf",
+ "call other.month.valueOf",
+ "get other.monthCode",
+ "get other.monthCode.toString",
+ "call other.monthCode.toString",
+ "get other.year",
+ "get other.year.valueOf",
+ "call other.year.valueOf",
+ "call other.calendar.dateFromFields",
+ // CalendarEquals
+ "get this.calendar.id",
+ "get other.calendar.id",
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.roundingIncrement",
+ "get options.roundingIncrement",
+ "getOwnPropertyDescriptor options.roundingMode",
+ "get options.roundingMode",
+ "getOwnPropertyDescriptor options.largestUnit",
+ "get options.largestUnit",
+ "getOwnPropertyDescriptor options.smallestUnit",
+ "get options.smallestUnit",
+ "getOwnPropertyDescriptor options.additional",
+ "get options.additional",
+ // GetDifferenceSettings
+ "get options.largestUnit.toString",
+ "call options.largestUnit.toString",
+ "get options.roundingIncrement.valueOf",
+ "call options.roundingIncrement.valueOf",
+ "get options.roundingMode.toString",
+ "call options.roundingMode.toString",
+ "get options.smallestUnit.toString",
+ "call options.smallestUnit.toString",
+];
+const actual = [];
+
+const ownCalendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainDate(2000, 5, 2, ownCalendar);
+
+const otherDatePropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 6,
+ monthCode: "M06",
+ day: 2,
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+
+function createOptionsObserver({ smallestUnit = "days", largestUnit = "auto", roundingMode = "halfExpand", roundingIncrement = 1 } = {}) {
+ return TemporalHelpers.propertyBagObserver(actual, {
+ // order is significant, due to iterating through properties in order to
+ // copy them to an internal null-prototype object:
+ roundingIncrement,
+ roundingMode,
+ largestUnit,
+ smallestUnit,
+ additional: "property",
+ }, "options");
+}
+
+// clear any observable things that happened while constructing the objects
+actual.splice(0);
+
+// basic order of observable operations with calendar call, without rounding:
+instance.until(otherDatePropertyBag, createOptionsObserver({ largestUnit: "years" }));
+assert.compareArray(actual, expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+]), "order of operations");
+actual.splice(0); // clear
+
+// short-circuit for identical objects:
+
+const identicalPropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 5,
+ monthCode: "M05",
+ day: 2,
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+
+instance.since(identicalPropertyBag, createOptionsObserver());
+assert.compareArray(actual, expected, "order of operations with identical dates");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year:
+const expectedOpsForYearRounding = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateAdd", // 12.d
+ "call this.calendar.dateAdd", // 12.f
+ "call this.calendar.dateUntil", // 12.n
+ "call this.calendar.dateAdd", // 12.x MoveRelativeDate
+ // (12.r not called because other units can't add up to >1 year at this point)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 9.c
+ "call this.calendar.dateUntil" // 9.d
+]);
+instance.until(otherDatePropertyBag, createOptionsObserver({ smallestUnit: "years" }));
+assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year and skips a DateUntil call:
+const otherDatePropertyBagSameMonth = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 5,
+ monthCode: "M05",
+ day: 2,
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+const expectedOpsForYearRoundingSameMonth = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateAdd", // 12.d
+ "call this.calendar.dateAdd", // 12.f
+ "call this.calendar.dateAdd", // 12.x MoveRelativeDate
+ // (12.n not called because months and weeks == 0)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 9.c
+ "call this.calendar.dateUntil" // 9.d
+]);
+instance.until(otherDatePropertyBagSameMonth, createOptionsObserver({ smallestUnit: "years" }));
+assert.compareArray(actual, expectedOpsForYearRoundingSameMonth, "order of operations with smallestUnit = years and no excess months/weeks");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest month:
+const expectedOpsForMonthRounding = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateAdd", // 13.c
+ "call this.calendar.dateAdd", // 13.e
+ "call this.calendar.dateAdd", // 13.w MoveRelativeDate
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 10.d
+ "call this.calendar.dateUntil" // 10.e
+]);
+instance.until(otherDatePropertyBag, createOptionsObserver({ smallestUnit: "months" }));
+assert.compareArray(actual, expectedOpsForMonthRounding, "order of operations with smallestUnit = months");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest week:
+const expectedOpsForWeekRounding = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateUntil", // 14.f
+ "call this.calendar.dateAdd", // 14.p MoveRelativeDate
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 16
+ "call this.calendar.dateUntil" // 17
+]);
+instance.until(otherDatePropertyBag, createOptionsObserver({ smallestUnit: "weeks" }));
+assert.compareArray(actual, expectedOpsForWeekRounding, "order of operations with smallestUnit = weeks");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/prop-desc.js
new file mode 100644
index 0000000000..0c230a999e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: The "until" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.until,
+ "function",
+ "`typeof PlainDate.prototype.until` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "until", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/round-cross-unit-boundary.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/round-cross-unit-boundary.js
new file mode 100644
index 0000000000..6b15e24902
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/round-cross-unit-boundary.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Rounding can cross unit boundaries up to largestUnit
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2022, 1, 1);
+const later = new Temporal.PlainDate(2023, 12, 25);
+const duration = earlier.until(later, { largestUnit: "years", smallestUnit: "months", roundingMode: "expand" });
+TemporalHelpers.assertDuration(duration, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "1 year 11 months balances to 2 years");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/rounding-relative.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/rounding-relative.js
new file mode 100644
index 0000000000..b7b28c9338
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/rounding-relative.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Should round relative to the receiver.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date1 = Temporal.PlainDate.from("2019-01-01");
+const date2 = Temporal.PlainDate.from("2019-02-15");
+
+TemporalHelpers.assertDuration(
+ date1.until(date2, { smallestUnit: "months", roundingMode: "halfExpand" }),
+ 0, /* months = */ 2, 0, 0, 0, 0, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(
+ date2.until(date1, { smallestUnit: "months", roundingMode: "halfExpand" }),
+ 0, /* months = */ -1, 0, 0, 0, 0, 0, 0, 0, 0);
+
+const cases = [
+ ["2019-03-01", "2019-01-29", 1, 1],
+ ["2019-01-29", "2019-03-01", -1, -3],
+ ["2019-03-29", "2019-01-30", 1, 29],
+ ["2019-01-30", "2019-03-29", -1, -29],
+ ["2019-03-30", "2019-01-31", 1, 30],
+ ["2019-01-31", "2019-03-30", -1, -28],
+ ["2019-03-31", "2019-01-31", 2, 0],
+ ["2019-01-31", "2019-03-31", -2, 0]
+];
+for (const [end, start, months, days] of cases) {
+ const result = Temporal.PlainDate.from(start).until(end, { largestUnit: "months" });
+ TemporalHelpers.assertDuration(result, 0, months, 0, days, 0, 0, 0, 0, 0, 0, `${end} - ${start}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/rounding-zero-year-month-week-length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/rounding-zero-year-month-week-length.js
new file mode 100644
index 0000000000..1d56c472c9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/rounding-zero-year-month-week-length.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: >
+ A malicious calendar resulting in a year, month, or week length of zero is
+ handled correctly
+info: |
+ RoundDuration
+ 10.z. If _oneYearDays_ = 0, throw a *RangeError* exception.
+ ...
+ 11.z. If _oneMonthDays_ = 0, throw a *RangeError* exception.
+ ...
+ 12.s. If _oneWeekDays_ = 0, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const cal = new class extends Temporal.Calendar {
+ dateAdd(date, duration, options) {
+ // Called several times, last call sets oneYear/Month/WeekDays to 0
+ return new Temporal.PlainDate(1970, 1, 1);
+ }
+}("iso8601");
+
+const d1 = new Temporal.PlainDate(1970, 1, 1, cal);
+const d2 = new Temporal.PlainDate(1971, 1, 1, cal);
+
+assert.throws(RangeError, () => d1.until(d2, { smallestUnit: "years" }), "zero year length handled correctly");
+assert.throws(RangeError, () => d1.until(d2, { smallestUnit: "months" }), "zero month length handled correctly");
+assert.throws(RangeError, () => d1.until(d2, { smallestUnit: "weeks" }), "zero week length handled correctly");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-nan.js
new file mode 100644
index 0000000000..7e68bdd813
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-nan.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: RangeError thrown when roundingIncrement option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaindate.prototype.until step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-non-integer.js
new file mode 100644
index 0000000000..349135ad08
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-non-integer.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Rounding for roundingIncrement option
+info: |
+ ToTemporalRoundingIncrement ( _normalizedOptions_ )
+
+ 1. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, *"number"*, *undefined*, *1*<sub>𝔽</sub>).
+ 2. If _increment_ is not finite, throw a *RangeError* exception.
+ 3. Let _integerIncrement_ be truncate(ℝ(_increment_)).
+ 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception.
+ 5. Return _integerIncrement_.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+const result = earlier.until(later, { roundingIncrement: 2.5, roundingMode: "trunc" });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, "roundingIncrement 2.5 truncates to 2");
+const result2 = earlier.until(later, { smallestUnit: "days", roundingIncrement: 1e9 + 0.5, roundingMode: "expand" });
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 1e9, 0, 0, 0, 0, 0, 0, "roundingIncrement 1e9 + 0.5 truncates to 1e9");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-out-of-range.js
new file mode 100644
index 0000000000..26092686df
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-out-of-range.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: RangeError thrown when roundingIncrement option out of range
+info: |
+ ToTemporalRoundingIncrement ( _normalizedOptions_ )
+
+ 1. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, *"number"*, *undefined*, *1*<sub>𝔽</sub>).
+ 2. If _increment_ is not finite, throw a *RangeError* exception.
+ 3. Let _integerIncrement_ be truncate(ℝ(_increment_)).
+ 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception.
+ 5. Return _integerIncrement_.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -1 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 0 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 0.9 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 1e9 + 1 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: Infinity }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-undefined.js
new file mode 100644
index 0000000000..7670614029
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-undefined.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Fallback value for roundingIncrement option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaindate.prototype.until step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+
+const explicit = earlier.until(later, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, "default roundingIncrement is 1");
+
+// See options-undefined.js for {}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-wrong-type.js
new file mode 100644
index 0000000000..c2cc289fb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-wrong-type.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Type conversions for roundingIncrement option
+info: |
+ sec-getoption step 8.a:
+ a. Set _value_ to ? ToNumber(value).
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaindate.prototype.until step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => earlier.until(later, { roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement.js
new file mode 100644
index 0000000000..5a86e247c8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Tests calculations with roundingMode "trunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = Temporal.PlainDate.from("2019-01-08");
+const later = Temporal.PlainDate.from("2021-09-07");
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "years", roundingIncrement: 4, roundingMode: "halfExpand" }),
+ /* years = */ 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, "years");
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "months", roundingIncrement: 10, roundingMode: "halfExpand" }),
+ 0, /* months = */ 30, 0, 0, 0, 0, 0, 0, 0, 0, "months");
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "weeks", roundingIncrement: 12, roundingMode: "halfExpand" }),
+ 0, 0, /* weeks = */ 144, 0, 0, 0, 0, 0, 0, 0, "weeks");
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "days", roundingIncrement: 100, roundingMode: "halfExpand" }),
+ 0, 0, 0, /* days = */ 1000, 0, 0, 0, 0, 0, 0, "days");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-ceil.js
new file mode 100644
index 0000000000..fe40fdd5f1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-ceil.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Tests calculations with roundingMode "ceil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [3], [-2]],
+ ["months", [0, 32], [0, -31]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+const roundingMode = "ceil";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-expand.js
new file mode 100644
index 0000000000..d3b3271b50
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-expand.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Tests calculations with roundingMode "expand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+const roundingMode = "expand";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-floor.js
new file mode 100644
index 0000000000..37c8a1c1a5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-floor.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Tests calculations with roundingMode "floor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [2], [-3]],
+ ["months", [0, 31], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+const roundingMode = "floor";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..55118dd705
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfCeil.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Tests calculations with roundingMode "halfCeil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+const roundingMode = "halfCeil";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfEven.js
new file mode 100644
index 0000000000..3b42461cdb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfEven.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Tests calculations with roundingMode "halfEven".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+const roundingMode = "halfEven";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..c5fc068b82
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfExpand.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Tests calculations with roundingMode "halfExpand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+const roundingMode = "halfExpand";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..b4f0c8f767
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfFloor.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Tests calculations with roundingMode "halfFloor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+const roundingMode = "halfFloor";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..abd790fe34
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfTrunc.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Tests calculations with roundingMode "halfTrunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+const roundingMode = "halfTrunc";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..7cc40d387e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-invalid-string.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) {
+ assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "microsecond", roundingMode }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-trunc.js
new file mode 100644
index 0000000000..43cbe4a1f0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-trunc.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Tests calculations with roundingMode "trunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [2], [-2]],
+ ["months", [0, 31], [0, -31]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+const roundingMode = "trunc";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-undefined.js
new file mode 100644
index 0000000000..0370a2e95a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-undefined.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 1, 1);
+
+const later1 = new Temporal.PlainDate(2005, 2, 20);
+const explicit1 = earlier.until(later1, { smallestUnit: "year", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+const implicit1 = earlier.until(later1, { smallestUnit: "year" });
+TemporalHelpers.assertDuration(implicit1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+
+const later2 = new Temporal.PlainDate(2005, 12, 15);
+const explicit2 = earlier.until(later2, { smallestUnit: "year", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+const implicit2 = earlier.until(later2, { smallestUnit: "year" });
+TemporalHelpers.assertDuration(implicit2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..0c95403f84
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-wrong-type.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => earlier.until(later, { smallestUnit: "year", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-higher-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-higher-units.js
new file mode 100644
index 0000000000..e6cd7b717a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-higher-units.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Tests calculations with higher smallestUnit than the default of "days"
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = Temporal.PlainDate.from("2019-01-08");
+const later = Temporal.PlainDate.from("2021-09-07");
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "years", roundingMode: "halfExpand" }),
+ /* years = */ 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, "years");
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "months", roundingMode: "halfExpand" }),
+ 0, /* months = */ 32, 0, 0, 0, 0, 0, 0, 0, 0, "months");
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "weeks", roundingMode: "halfExpand" }),
+ 0, 0, /* weeks = */ 139, 0, 0, 0, 0, 0, 0, 0, "weeks");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..6979d8f13c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-invalid-string.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+const badValues = [
+ "era",
+ "eraYear",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+ "month\0",
+ "YEAR",
+ "eras",
+ "eraYears",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+ "months\0",
+ "YEARS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => earlier.until(later, { smallestUnit }),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..75f6d73609
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-plurals-accepted.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 12);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => earlier.until(later, { smallestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-undefined.js
new file mode 100644
index 0000000000..359541c4f2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-undefined.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+
+const explicit = earlier.until(later, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 397, 0, 0, 0, 0, 0, 0, "default smallestUnit is day");
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 397, 0, 0, 0, 0, 0, 0, "default smallestUnit is day");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..f257997db1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-wrong-type.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "year",
+ (smallestUnit) => earlier.until(later, { smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/weeks-months.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/weeks-months.js
new file mode 100644
index 0000000000..54dbd17bc1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/weeks-months.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: until() should not return weeks and months together.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(1969, 7, 24);
+const laterDate = new Temporal.PlainDate(1969, 9, 4);
+TemporalHelpers.assertDuration(date.until(laterDate, { largestUnit: "weeks" }),
+ 0, 0, /* weeks = */ 6, 0, 0, 0, 0, 0, 0, 0, "weeks");
+TemporalHelpers.assertDuration(date.until(laterDate, { largestUnit: "months" }),
+ 0, /* months = */ 1, 0, 11, 0, 0, 0, 0, 0, 0, "months");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/year-zero.js
new file mode 100644
index 0000000000..f92ca0a5e4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/year-zero.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/basic.js
new file mode 100644
index 0000000000..3b8a525f93
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/basic.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.valueof
+description: Basic tests for valueOf().
+features: [Temporal]
+---*/
+
+const plainDate = Temporal.PlainDate.from("1963-02-13");
+const plainDate2 = Temporal.PlainDate.from("1963-02-13");
+
+assert.throws(TypeError, () => plainDate.valueOf(), "valueOf");
+assert.throws(TypeError, () => plainDate < plainDate, "<");
+assert.throws(TypeError, () => plainDate <= plainDate, "<=");
+assert.throws(TypeError, () => plainDate > plainDate, ">");
+assert.throws(TypeError, () => plainDate >= plainDate, ">=");
+assert.sameValue(plainDate === plainDate, true, "===");
+assert.sameValue(plainDate === plainDate2, false, "===");
+assert.sameValue(plainDate !== plainDate, false, "!==");
+assert.sameValue(plainDate !== plainDate2, true, "!==");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/branding.js
new file mode 100644
index 0000000000..f3d9029747
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.valueof
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const valueOf = Temporal.PlainDate.prototype.valueOf;
+
+assert.sameValue(typeof valueOf, "function");
+
+assert.throws(TypeError, () => valueOf.call(undefined), "undefined");
+assert.throws(TypeError, () => valueOf.call(null), "null");
+assert.throws(TypeError, () => valueOf.call(true), "true");
+assert.throws(TypeError, () => valueOf.call(""), "empty string");
+assert.throws(TypeError, () => valueOf.call(Symbol()), "symbol");
+assert.throws(TypeError, () => valueOf.call(1), "1");
+assert.throws(TypeError, () => valueOf.call({}), "plain object");
+assert.throws(TypeError, () => valueOf.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => valueOf.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/builtin.js
new file mode 100644
index 0000000000..04ce6abb81
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.valueof
+description: >
+ Tests that Temporal.PlainDate.prototype.valueOf
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.valueOf),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.valueOf),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.valueOf),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.valueOf.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/length.js
new file mode 100644
index 0000000000..3460276c29
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.valueof
+description: Temporal.PlainDate.prototype.valueOf.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.valueOf, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/name.js
new file mode 100644
index 0000000000..a42c0be477
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.valueof
+description: Temporal.PlainDate.prototype.valueOf.name is "valueOf".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.valueOf, "name", {
+ value: "valueOf",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/not-a-constructor.js
new file mode 100644
index 0000000000..20de55f8c5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.valueof
+description: >
+ Temporal.PlainDate.prototype.valueOf does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.valueOf();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.valueOf), false,
+ "isConstructor(Temporal.PlainDate.prototype.valueOf)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/prop-desc.js
new file mode 100644
index 0000000000..fc586ef792
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.valueof
+description: The "valueOf" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.valueOf,
+ "function",
+ "`typeof PlainDate.prototype.valueOf` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "valueOf", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/basic.js
new file mode 100644
index 0000000000..0e60fc7e81
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/basic.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.weekofyear
+description: Basic tests for weekOfYear().
+features: [Temporal]
+---*/
+
+for (let i = 29; i <= 31; ++i) {
+ const plainDate = new Temporal.PlainDate(1975, 12, i);
+ assert.sameValue(plainDate.weekOfYear, 1, `${plainDate} should be in week 1`);
+}
+for (let i = 1; i <= 4; ++i) {
+ const plainDate = new Temporal.PlainDate(1976, 1, i);
+ assert.sameValue(plainDate.weekOfYear, 1, `${plainDate} should be in week 1`);
+}
+for (let i = 5; i <= 11; ++i) {
+ const plainDate = new Temporal.PlainDate(1976, 1, i);
+ assert.sameValue(plainDate.weekOfYear, 2, `${plainDate} should be in week 2`);
+}
+for (let i = 20; i <= 26; ++i) {
+ const plainDate = new Temporal.PlainDate(1976, 12, i);
+ assert.sameValue(plainDate.weekOfYear, 52, `${plainDate} should be in week 52`);
+}
+for (let i = 27; i <= 31; ++i) {
+ const plainDate = new Temporal.PlainDate(1976, 12, i);
+ assert.sameValue(plainDate.weekOfYear, 53, `${plainDate} should be in week 53`);
+}
+for (let i = 1; i <= 2; ++i) {
+ const plainDate = new Temporal.PlainDate(1977, 1, i);
+ assert.sameValue(plainDate.weekOfYear, 53, `${plainDate} should be in week 53`);
+}
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/branding.js
new file mode 100644
index 0000000000..cb26e7db4b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.weekofyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const weekOfYear = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "weekOfYear").get;
+
+assert.sameValue(typeof weekOfYear, "function");
+
+assert.throws(TypeError, () => weekOfYear.call(undefined), "undefined");
+assert.throws(TypeError, () => weekOfYear.call(null), "null");
+assert.throws(TypeError, () => weekOfYear.call(true), "true");
+assert.throws(TypeError, () => weekOfYear.call(""), "empty string");
+assert.throws(TypeError, () => weekOfYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => weekOfYear.call(1), "1");
+assert.throws(TypeError, () => weekOfYear.call({}), "plain object");
+assert.throws(TypeError, () => weekOfYear.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => weekOfYear.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..7a8e2479c9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.weekofyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const weekOfYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "weekOfYear");
+Object.defineProperty(Temporal.Calendar.prototype, "weekOfYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("weekOfYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.weekOfYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "weekOfYear", weekOfYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/custom.js
new file mode 100644
index 0000000000..1604da720c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.weekofyear
+description: Custom calendar tests for weekOfYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ weekOfYear(...args) {
+ ++calls;
+ assert.compareArray(args, [pd], "weekOfYear arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pd = new Temporal.PlainDate(1830, 8, 25, calendar);
+const result = pd.weekOfYear;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/prop-desc.js
new file mode 100644
index 0000000000..b1dde97e11
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.weekofyear
+description: The "weekOfYear" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "weekOfYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/validate-calendar-value.js
new file mode 100644
index 0000000000..7836bbff10
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/validate-calendar-value.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.weekofyear
+description: Validate result returned from calendar weekOfYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ weekOfYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.throws(error, () => instance.weekOfYear, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/basic.js
new file mode 100644
index 0000000000..cf5e85bd1d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/basic.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: Basic tests for with().
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(1976, 11, 18);
+
+const withYear = plainDate.with({ year: 2019 });
+TemporalHelpers.assertPlainDate(withYear, 2019, 11, "M11", 18, "with(year)");
+
+const withMonth = plainDate.with({ month: 5 });
+TemporalHelpers.assertPlainDate(withMonth, 1976, 5, "M05", 18, "with(month)");
+
+const withMonthCode = plainDate.with({ monthCode: 'M05' });
+TemporalHelpers.assertPlainDate(withMonthCode, 1976, 5, "M05", 18, "with(monthCode)");
+
+const withDay = plainDate.with({ day: 17 });
+TemporalHelpers.assertPlainDate(withDay, 1976, 11, "M11", 17, "with(day)");
+
+const withPlural = plainDate.with({ months: 12, day: 15 });
+TemporalHelpers.assertPlainDate(withPlural, 1976, 11, "M11", 15, "with(plural)");
+
+assert.throws(RangeError, () => plainDate.with({ month: 5, monthCode: 'M06' }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/branding.js
new file mode 100644
index 0000000000..bd2d266e41
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const with_ = Temporal.PlainDate.prototype.with;
+
+assert.sameValue(typeof with_, "function");
+
+const args = [{ year: 2022 }];
+
+assert.throws(TypeError, () => with_.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => with_.apply(null, args), "null");
+assert.throws(TypeError, () => with_.apply(true, args), "true");
+assert.throws(TypeError, () => with_.apply("", args), "empty string");
+assert.throws(TypeError, () => with_.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => with_.apply(1, args), "1");
+assert.throws(TypeError, () => with_.apply({}, args), "plain object");
+assert.throws(TypeError, () => with_.apply(Temporal.PlainDate, args), "Temporal.PlainDate");
+assert.throws(TypeError, () => with_.apply(Temporal.PlainDate.prototype, args), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..4e4d090692
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainDate(2023, 5, 2, "iso8601");
+instance.with({ day: 5 });
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..94beab4fd1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,46 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const fieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "fields");
+Object.defineProperty(Temporal.Calendar.prototype, "fields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("fields should not be looked up");
+ },
+});
+const mergeFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "mergeFields");
+Object.defineProperty(Temporal.Calendar.prototype, "mergeFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("mergeFields should not be looked up");
+ },
+});
+const dateFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateFromFields should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.with({ year: 2001 });
+
+Object.defineProperty(Temporal.Calendar.prototype, "fields", fieldsOriginal);
+Object.defineProperty(Temporal.Calendar.prototype, "mergeFields", mergeFieldsOriginal);
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", dateFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/builtin.js
new file mode 100644
index 0000000000..d356db6b7d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: >
+ Tests that Temporal.PlainDate.prototype.with
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.with),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.with),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.with),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.with.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-fields-iterable.js
new file mode 100644
index 0000000000..2fd26bafa7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-fields-iterable.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindate.prototype.with step 9:
+ 9. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const date = new Temporal.PlainDate(2000, 5, 2, calendar);
+date.with({ year: 2005 });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..36242e3577
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+instance.with({ day: 24 });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-invalid-return.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-invalid-return.js
new file mode 100644
index 0000000000..9555916651
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-invalid-return.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: Throw when the returned value from the calendar's dateFromFields method is not a PlainDate.
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor(value) {
+ super("iso8601");
+ this.value = value;
+ }
+ dateFromFields() {
+ return this.value;
+ }
+}
+
+const tests = [
+ [undefined],
+ [null, "null"],
+ [true],
+ ["2000-05"],
+ [Symbol()],
+ [200005],
+ [200005n],
+ [{}, "plain object"],
+ [() => {}, "lambda"],
+ [Temporal.PlainDate, "Temporal.PlainDate"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype"],
+];
+for (const [test, description = typeof test] of tests) {
+ const plainDate = new Temporal.PlainDate(2000, 5, 2, new CustomCalendar(test));
+ assert.throws(TypeError, () => plainDate.with({ year: 1 }), `Expected error with ${description}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-merge-fields-returns-primitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-merge-fields-returns-primitive.js
new file mode 100644
index 0000000000..80de0a1a3d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-merge-fields-returns-primitive.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: >
+ with() should throw a TypeError if mergeFields() returns a primitive,
+ without passing the value on to any other calendar methods
+includes: [compareArray.js, temporalHelpers.js]
+features: [BigInt, Symbol, Temporal]
+---*/
+
+[undefined, null, true, 3.14159, "bad value", Symbol("no"), 7n].forEach((primitive) => {
+ const calendar = TemporalHelpers.calendarMergeFieldsReturnsPrimitive(primitive);
+ const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+ assert.throws(TypeError, () => instance.with({ year: 2005 }), "bad return from mergeFields() throws");
+ assert.sameValue(calendar.dateFromFieldsCallCount, 0, "dateFromFields() never called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..a02da126df
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: >
+ Calendar.mergeFields method is called with null-prototype fields objects
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckMergeFieldsPrototypePollution();
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+instance.with({ day: 24 });
+assert.sameValue(calendar.mergeFieldsCallCount, 1, "mergeFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..ec53ff16a7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const date = new Temporal.PlainDate(2023, 5, 1, calendar);
+
+assert.throws(RangeError, () => date.with({day: 15}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/copies-merge-fields-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/copies-merge-fields-object.js
new file mode 100644
index 0000000000..ac83620275
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/copies-merge-fields-object.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: The object returned from mergeFields() is copied before being passed to dateFromFields().
+info: |
+ sec-temporal.plaindate.prototype.with steps 13–15:
+ 13. Set _fields_ to ? CalendarMergeFields(_calendar_, _fields_, _partialDate_).
+ 14. Set _fields_ to ? PrepareTemporalFields(_fields_, _fieldNames_, «»).
+ 15. Return ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get day",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get month",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get monthCode",
+ "get monthCode.toString",
+ "call monthCode.toString",
+ "get year",
+ "get year.valueOf",
+ "call year.valueOf",
+];
+
+const calendar = TemporalHelpers.calendarMergeFieldsGetters();
+const date = new Temporal.PlainDate(2021, 3, 31, calendar);
+date.with({ year: 2022 });
+
+assert.compareArray(calendar.mergeFieldsReturnOperations, expected, "getters called on mergeFields return");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/copy-properties-not-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/copy-properties-not-undefined.js
new file mode 100644
index 0000000000..b7dde1f9af
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/copy-properties-not-undefined.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: PreparePartialTemporalFields copies only defined properties of source object
+info: |
+ 4. For each value _property_ of _fieldNames_, do
+ a. Let _value_ be ? Get(_fields_, _property_).
+ b. If _value_ is not *undefined*, then
+ ...
+ iii. Perform ! CreateDataPropertyOrThrow(_result_, _property_, _value_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2006, 1, 24);
+
+TemporalHelpers.assertPlainDate(plainDate.with({ day: 1, year: undefined }),
+ 2006, 1, "M01", 1,
+ "only the properties that are present and defined in the plain object are copied"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/custom.js
new file mode 100644
index 0000000000..6efb016438
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/custom.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: Basic tests with custom calendar
+features: [Temporal]
+---*/
+
+const result = new Temporal.PlainDate(1920, 5, 3);
+const options = {
+ extra: "property",
+};
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateFromFields(...args) {
+ ++calls;
+ assert.sameValue(args.length, 2, "Two arguments");
+ assert.sameValue(typeof args[0], "object", "First argument: type");
+ assert.sameValue(args[0].day, 18, "First argument: day");
+ assert.sameValue(args[0].month, 11, "First argument: month");
+ assert.sameValue(args[0].monthCode, "M11", "First argument: monthCode");
+ assert.sameValue(args[0].year, 43, "First argument: year");
+ assert.notSameValue(args[1], options, "Second argument is a copy of options");
+ assert.sameValue(args[1].extra, "property", "All properties are copied");
+ assert.sameValue(Object.getPrototypeOf(args[1]), null, "Copy has null prototype");
+ return result;
+ }
+}
+const calendar = new CustomCalendar();
+const plainDate = new Temporal.PlainDate(1976, 11, 18, calendar);
+assert.sameValue(plainDate.with({ year: 43 }, options), result);
+assert.sameValue(calls, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..8e1628294d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/duplicate-calendar-fields.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['monthCode'], ['day'], ['month'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const date = new Temporal.PlainDate(2023, 5, 1, calendar);
+
+ assert.throws(RangeError, () => date.with({day: 15}));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..aa6e168ea7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/infinity-throws-rangeerror.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindate.prototype.with
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => instance.with({ [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.with({ [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/length.js
new file mode 100644
index 0000000000..22beee3de6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: Temporal.PlainDate.prototype.with.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.with, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/name.js
new file mode 100644
index 0000000000..8cc69ee18d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: Temporal.PlainDate.prototype.with.name is "with".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.with, "name", {
+ value: "with",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/not-a-constructor.js
new file mode 100644
index 0000000000..c730e54a2b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: >
+ Temporal.PlainDate.prototype.with does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.with();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.with), false,
+ "isConstructor(Temporal.PlainDate.prototype.with)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/options-object.js
new file mode 100644
index 0000000000..52ec0f1d8c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/options-object.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const result1 = instance.with({ day: 5 }, {});
+TemporalHelpers.assertPlainDate(
+ result1, 2000, 5, "M05", 5,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.with({ day: 5 }, () => {});
+TemporalHelpers.assertPlainDate(
+ result2, 2000, 5, "M05", 5,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/options-undefined.js
new file mode 100644
index 0000000000..99efdcbf51
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/options-undefined.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 2, 2);
+const fields = { day: 31 };
+
+const explicit = date.with(fields, undefined);
+assert.sameValue(explicit.month, 2, "default overflow is constrain");
+assert.sameValue(explicit.day, 29, "default overflow is constrain");
+
+const implicit = date.with(fields);
+assert.sameValue(implicit.month, 2, "default overflow is constrain");
+assert.sameValue(implicit.day, 29, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/options-wrong-type.js
new file mode 100644
index 0000000000..ed53fa255e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/options-wrong-type.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.with({ day: 5 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/order-of-operations.js
new file mode 100644
index 0000000000..5601354e15
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/order-of-operations.js
@@ -0,0 +1,80 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: Properties on an object passed to with() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // RejectObjectWithCalendarOrTimeZone
+ "get fields.calendar",
+ "get fields.timeZone",
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ "getOwnPropertyDescriptor options.extra",
+ "get options.extra",
+ // lookup
+ "get this.calendar.dateFromFields",
+ "get this.calendar.fields",
+ "get this.calendar.mergeFields",
+ // CalendarFields
+ "call this.calendar.fields",
+ // PrepareTemporalFields on receiver
+ "get this.calendar.day",
+ "call this.calendar.day",
+ "get this.calendar.month",
+ "call this.calendar.month",
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ "get this.calendar.year",
+ "call this.calendar.year",
+ // PrepareTemporalFields on argument
+ "get fields.day",
+ "get fields.day.valueOf",
+ "call fields.day.valueOf",
+ "get fields.month",
+ "get fields.month.valueOf",
+ "call fields.month.valueOf",
+ "get fields.monthCode",
+ "get fields.monthCode.toString",
+ "call fields.monthCode.toString",
+ "get fields.year",
+ "get fields.year.valueOf",
+ "call fields.year.valueOf",
+ // CalendarMergeFields
+ "call this.calendar.mergeFields",
+ // CalendarDateFromFields
+ "call this.calendar.dateFromFields",
+ // inside Calendar.p.dateFromFields
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ year: 1.7,
+ month: 1.7,
+ monthCode: "M01",
+ day: 1.7,
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ overflow: "constrain",
+ extra: "property",
+}, "options");
+
+instance.with(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/overflow-invalid-string.js
new file mode 100644
index 0000000000..725da2cbff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/overflow-invalid-string.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isodatefromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.prototype.with step 16:
+ 16. Return ? DateFromFields(_calendar_, _fields_, _options_).
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => date.with({ month: 8 }, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/overflow-undefined.js
new file mode 100644
index 0000000000..52768ac466
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/overflow-undefined.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isodatefromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.prototype.with step 16:
+ 16. Return ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const explicit = date.with({ month: 15 }, { overflow: undefined });
+TemporalHelpers.assertPlainDate(explicit, 2000, 12, "M12", 2, "default overflow is constrain");
+const implicit = date.with({ month: 15 }, {});
+TemporalHelpers.assertPlainDate(implicit, 2000, 12, "M12", 2, "default overflow is constrain");
+const fun = date.with({ month: 15 }, () => {});
+TemporalHelpers.assertPlainDate(fun, 2000, 12, "M12", 2, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/overflow-wrong-type.js
new file mode 100644
index 0000000000..bc53f043ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/overflow-wrong-type.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isodatefromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.prototype.with step 16:
+ 16. Return ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => date.with({ month: 8 }, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainDate(result, 2000, 8, "M08", 2, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/plaindatelike-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/plaindatelike-invalid.js
new file mode 100644
index 0000000000..4d1e422690
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/plaindatelike-invalid.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: Throws TypeError on an argument that is not a PlainDate-like property bag
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(1976, 11, 18);
+
+const tests = [
+ // Step 3.
+ [undefined],
+ [null],
+ [true],
+ ["2019-05-17"],
+ ["2019-05-17T12:34"],
+ ["2019-05-17T12:34Z"],
+ ["18:05:42.577"],
+ ["42"],
+ [Symbol(), "symbol"],
+ [42, "number"],
+ [42n, "bigint"],
+
+ // Step 4.
+ // RejectObjectWithCalendarOrTimeZone step 2.
+ [Temporal.PlainDate.from("2019-05-17"), "PlainDate"],
+ [Temporal.PlainDateTime.from("2019-05-17T12:34"), "PlainDateTime"],
+ [Temporal.PlainMonthDay.from("2019-05-17"), "PlainMonthDay"],
+ [Temporal.PlainTime.from("12:34"), "PlainTime"],
+ [Temporal.PlainYearMonth.from("2019-05-17"), "PlainYearMonth"],
+ [Temporal.ZonedDateTime.from("2019-05-17T12:34Z[UTC]"), "ZonedDateTime"],
+ // RejectObjectWithCalendarOrTimeZone step 3-4.
+ [{ year: 2021, calendar: "iso8601" }, "calendar"],
+ // RejectObjectWithCalendarOrTimeZone step 5-6.
+ [{ year: 2021, timeZone: "UTC" }, "timeZone"],
+
+ // Step 7.
+ [{}, "empty object"],
+ [{ months: 12 }, "only plural property"],
+
+];
+
+for (const [value, message = String(value)] of tests) {
+ assert.throws(TypeError, () => plainDate.with(value), message);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/prop-desc.js
new file mode 100644
index 0000000000..857b8e17a7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: The "with" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.with,
+ "function",
+ "`typeof PlainDate.prototype.with` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "with", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..68eef30019
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const date = new Temporal.PlainDate(2023, 5, 1, calendar);
+
+assert.throws(RangeError, () => date.with({day: 15}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/subclassing-ignored.js
new file mode 100644
index 0000000000..65b263f341
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/subclassing-ignored.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: Objects of a subclass are never created as return values for with()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDate,
+ [2000, 5, 2],
+ "with",
+ [{ day: 20 }],
+ (result) => TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 20),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/basic.js
new file mode 100644
index 0000000000..daafac19f2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/basic.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.withcalendar
+description: Basic tests for withCalendar().
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate = Temporal.PlainDate.from("1976-11-18");
+const calendar = Temporal.Calendar.from("iso8601");
+
+const objectResult = plainDate.withCalendar(calendar);
+assert.notSameValue(objectResult, plainDate, "object: new object");
+TemporalHelpers.assertPlainDate(objectResult, 1976, 11, "M11", 18, "object");
+assert.sameValue(objectResult.getCalendar(), calendar, "object: calendar");
+
+const stringResult = plainDate.withCalendar("iso8601");
+assert.notSameValue(stringResult, plainDate, "string: new object");
+TemporalHelpers.assertPlainDate(stringResult, 1976, 11, "M11", 18, "string");
+assert.sameValue(stringResult.getISOFields().calendar, "iso8601", "string: calendar slot stores a string");
+
+const originalCalendar = plainDate.getCalendar();
+const sameResult = plainDate.withCalendar(originalCalendar);
+assert.notSameValue(sameResult, plainDate, "original: new object");
+TemporalHelpers.assertPlainDate(sameResult, 1976, 11, "M11", 18, "original");
+assert.sameValue(sameResult.getCalendar(), originalCalendar, "original: calendar slot stores and object");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/branding.js
new file mode 100644
index 0000000000..b12a12f12d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.withcalendar
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const withCalendar = Temporal.PlainDate.prototype.withCalendar;
+
+assert.sameValue(typeof withCalendar, "function");
+
+const args = [new Temporal.Calendar("iso8601")];
+
+assert.throws(TypeError, () => withCalendar.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => withCalendar.apply(null, args), "null");
+assert.throws(TypeError, () => withCalendar.apply(true, args), "true");
+assert.throws(TypeError, () => withCalendar.apply("", args), "empty string");
+assert.throws(TypeError, () => withCalendar.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => withCalendar.apply(1, args), "1");
+assert.throws(TypeError, () => withCalendar.apply({}, args), "plain object");
+assert.throws(TypeError, () => withCalendar.apply(Temporal.PlainDate, args), "Temporal.PlainDate");
+assert.throws(TypeError, () => withCalendar.apply(Temporal.PlainDate.prototype, args), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..7869dea1f9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.withcalendar
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.withCalendar("iso8601");
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/builtin.js
new file mode 100644
index 0000000000..92c0aab861
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.withcalendar
+description: >
+ Tests that Temporal.PlainDate.prototype.withCalendar
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.withCalendar),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.withCalendar),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.withCalendar),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.withCalendar.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-case-insensitive.js
new file mode 100644
index 0000000000..a367f57bf4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-case-insensitive.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.withcalendar
+description: Calendar names are case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18, {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+
+const arg = "iSo8601";
+const result = instance.withCalendar(arg);
+assert.sameValue(result.calendarId, "iso8601", "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-number.js
new file mode 100644
index 0000000000..94eb9a3d7b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-number.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.withcalendar
+description: A number is not allowed to be a calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18, {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+
+const numbers = [
+ 1,
+ -19761118,
+ 19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.withCalendar(arg),
+ "A number is not a valid ISO string for Calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-string-leap-second.js
new file mode 100644
index 0000000000..5c8fc1773b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-string-leap-second.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.withcalendar
+description: Leap second is a valid ISO string for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18, {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+
+const arg = "2016-12-31T23:59:60";
+const result = instance.withCalendar(arg);
+assert.sameValue(
+ result.calendarId,
+ "iso8601",
+ "leap second is a valid ISO string for Calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-string.js
new file mode 100644
index 0000000000..8fb978666d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-string.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.withcalendar
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18, {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+
+const arg = "iso8601";
+
+const result = instance.withCalendar(arg);
+assert.sameValue(result.getISOFields().calendar, "iso8601", `Calendar created from string "${arg}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-temporal-object.js
new file mode 100644
index 0000000000..31039caed6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-temporal-object.js
@@ -0,0 +1,64 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.withcalendar
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal-totemporalcalendar step 1.b:
+ b. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2000, 5, 2);
+const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const plainMonthDay = new Temporal.PlainMonthDay(5, 2);
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 5);
+const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+[plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((arg) => {
+ const actual = [];
+ const expected = [];
+
+ const calendar = arg.getISOFields().calendar;
+
+ Object.defineProperty(arg, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ const instance = new Temporal.PlainDate(1976, 11, 18, {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+ const result = instance.withCalendar(arg);
+ assert.sameValue(result.getISOFields().calendar, calendar, "Temporal object coerced to calendar");
+
+ assert.compareArray(actual, expected, "calendar getter not called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-wrong-type.js
new file mode 100644
index 0000000000..c6184f42b7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-wrong-type.js
@@ -0,0 +1,64 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.withcalendar
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for Calendar
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18, {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => instance.withCalendar(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.withCalendar(arg), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/length.js
new file mode 100644
index 0000000000..1c0d85af21
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.withcalendar
+description: Temporal.PlainDate.prototype.withCalendar.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.withCalendar, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/missing-argument.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/missing-argument.js
new file mode 100644
index 0000000000..c8d2409041
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/missing-argument.js
@@ -0,0 +1,15 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.withcalendar
+description: TypeError thrown when calendar argument not given
+features: [Temporal]
+---*/
+
+const plainDate = Temporal.PlainDate.from("1976-11-18");
+assert.throws(TypeError, () => plainDate.withCalendar(), "missing argument");
+assert.throws(TypeError, () => plainDate.withCalendar(undefined), "undefined argument");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/name.js
new file mode 100644
index 0000000000..ff2da63f67
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.withcalendar
+description: Temporal.PlainDate.prototype.withCalendar.name is "withCalendar".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.withCalendar, "name", {
+ value: "withCalendar",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/not-a-constructor.js
new file mode 100644
index 0000000000..222f96e84a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.withcalendar
+description: >
+ Temporal.PlainDate.prototype.withCalendar does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.withCalendar();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.withCalendar), false,
+ "isConstructor(Temporal.PlainDate.prototype.withCalendar)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/prop-desc.js
new file mode 100644
index 0000000000..3cb792447e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.withcalendar
+description: The "withCalendar" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.withCalendar,
+ "function",
+ "`typeof PlainDate.prototype.withCalendar` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "withCalendar", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/subclassing-ignored.js
new file mode 100644
index 0000000000..c188e3fe7a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/subclassing-ignored.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.withcalendar
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const customCalendar = {
+ era() { return undefined; },
+ eraYear() { return undefined; },
+ year() { return 1900; },
+ month() { return 2; },
+ monthCode() { return "M02"; },
+ day() { return 5; },
+ id: "custom-calendar",
+ toString() { return "custom-calendar"; },
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDate,
+ [2000, 5, 2],
+ "withCalendar",
+ [customCalendar],
+ (result) => {
+ TemporalHelpers.assertPlainDate(result, 1900, 2, "M02", 5);
+ assert.sameValue(result.getCalendar(), customCalendar, "calendar result");
+ },
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/branding.js
new file mode 100644
index 0000000000..f41cf4a738
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.year
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const year = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "year").get;
+
+assert.sameValue(typeof year, "function");
+
+assert.throws(TypeError, () => year.call(undefined), "undefined");
+assert.throws(TypeError, () => year.call(null), "null");
+assert.throws(TypeError, () => year.call(true), "true");
+assert.throws(TypeError, () => year.call(""), "empty string");
+assert.throws(TypeError, () => year.call(Symbol()), "symbol");
+assert.throws(TypeError, () => year.call(1), "1");
+assert.throws(TypeError, () => year.call({}), "plain object");
+assert.throws(TypeError, () => year.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => year.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..c17301105f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.year
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const yearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "year");
+Object.defineProperty(Temporal.Calendar.prototype, "year", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("year should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.year;
+
+Object.defineProperty(Temporal.Calendar.prototype, "year", yearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/custom.js
new file mode 100644
index 0000000000..a333cff4e0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.year
+description: Custom calendar tests for year().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ year(...args) {
+ ++calls;
+ assert.compareArray(args, [pd], "year arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pd = new Temporal.PlainDate(1830, 8, 25, calendar);
+const result = pd.year;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/prop-desc.js
new file mode 100644
index 0000000000..45c773b912
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.year
+description: The "year" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "year");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/validate-calendar-value.js
new file mode 100644
index 0000000000..cf014e9017
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/validate-calendar-value.js
@@ -0,0 +1,54 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.year
+description: Validate result returned from calendar year() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [NaN, RangeError],
+ ["string", TypeError],
+ [{}, TypeError],
+ [null, TypeError],
+ [true, TypeError],
+ [false, TypeError],
+ [7.1, RangeError],
+ [-0.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ year() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.throws(error, () => instance.year, `${typeof result} ${String(result)} not converted to integer`);
+});
+
+const preservedResults = [
+ -7,
+];
+
+preservedResults.forEach(result => {
+ const calendar = new class extends Temporal.Calendar {
+ year() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.sameValue(instance.year, result, `${typeof result} ${String(result)} preserved`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/basic.js
new file mode 100644
index 0000000000..f5ef96ddb5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/basic.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.yearofweek
+description: Basic tests for yearOfWeek().
+features: [Temporal]
+---*/
+
+for (let i = 29; i <= 31; ++i) {
+ const plainDate = new Temporal.PlainDate(1975, 12, i);
+ assert.sameValue(plainDate.yearOfWeek, 1976, `${plainDate} should be in yearOfWeek 1976`);
+}
+for (let i = 1; i <= 11; ++i) {
+ const plainDate = new Temporal.PlainDate(1976, 1, i);
+ assert.sameValue(plainDate.yearOfWeek, 1976, `${plainDate} should be in yearOfWeek 1976`);
+}
+for (let i = 20; i <= 31; ++i) {
+ const plainDate = new Temporal.PlainDate(1976, 12, i);
+ assert.sameValue(plainDate.yearOfWeek, 1976, `${plainDate} should be in yearOfWeek 1976`);
+}
+for (let i = 1; i <= 2; ++i) {
+ const plainDate = new Temporal.PlainDate(1977, 1, i);
+ assert.sameValue(plainDate.yearOfWeek, 1976, `${plainDate} should be in yearOfWeek 1976`);
+}
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/branding.js
new file mode 100644
index 0000000000..6ade02cfdd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.yearofweek
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const yearOfWeek = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "yearOfWeek").get;
+
+assert.sameValue(typeof yearOfWeek, "function");
+
+assert.throws(TypeError, () => yearOfWeek.call(undefined), "undefined");
+assert.throws(TypeError, () => yearOfWeek.call(null), "null");
+assert.throws(TypeError, () => yearOfWeek.call(true), "true");
+assert.throws(TypeError, () => yearOfWeek.call(""), "empty string");
+assert.throws(TypeError, () => yearOfWeek.call(Symbol()), "symbol");
+assert.throws(TypeError, () => yearOfWeek.call(1), "1");
+assert.throws(TypeError, () => yearOfWeek.call({}), "plain object");
+assert.throws(TypeError, () => yearOfWeek.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => yearOfWeek.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..e9894eacf6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.yearofweek
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const yearOfWeekOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "yearOfWeek");
+Object.defineProperty(Temporal.Calendar.prototype, "yearOfWeek", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("yearOfWeek should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.yearOfWeek;
+
+Object.defineProperty(Temporal.Calendar.prototype, "yearOfWeek", yearOfWeekOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/custom.js
new file mode 100644
index 0000000000..bbcec99fa4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.yearofweek
+description: Custom calendar tests for yearOfWeek().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ yearOfWeek(...args) {
+ ++calls;
+ assert.compareArray(args, [pd], "yearOfWeek arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pd = new Temporal.PlainDate(1830, 8, 25, calendar);
+const result = pd.yearOfWeek;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/prop-desc.js
new file mode 100644
index 0000000000..1e0463d2c0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.yearofweek
+description: The "yearOfWeek" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "yearOfWeek");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/validate-calendar-value.js
new file mode 100644
index 0000000000..ec291bd0ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/validate-calendar-value.js
@@ -0,0 +1,55 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.yearofweek
+description: Validate result returned from calendar yearOfWeek() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [NaN, RangeError],
+ ["string", TypeError],
+ [{}, TypeError],
+ [null, TypeError],
+ [true, TypeError],
+ [false, TypeError],
+ [7.1, RangeError],
+ [-0.1, RangeError],
+ [NaN, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ yearOfWeek() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.throws(error, () => instance.yearOfWeek, `${typeof result} ${String(result)} not converted to integer`);
+});
+
+const preservedResults = [
+ -7,
+];
+
+preservedResults.forEach(result => {
+ const calendar = new class extends Temporal.Calendar {
+ yearOfWeek() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.sameValue(instance.yearOfWeek, result, `${typeof result} ${String(result)} preserved`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/subclass.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/subclass.js
new file mode 100644
index 0000000000..a268bb672d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/subclass.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate
+description: Test for Temporal.PlainDate subclassing.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomPlainDate extends Temporal.PlainDate {
+}
+
+const instance = new CustomPlainDate(2000, 5, 2);
+TemporalHelpers.assertPlainDate(instance, 2000, 5, "M05", 2);
+assert.sameValue(Object.getPrototypeOf(instance), CustomPlainDate.prototype, "Instance of CustomPlainDate");
+assert(instance instanceof CustomPlainDate, "Instance of CustomPlainDate");
+assert(instance instanceof Temporal.PlainDate, "Instance of Temporal.PlainDate");
+
+reportCompare(0, 0);