summaryrefslogtreecommitdiffstats
path: root/js/src/tests/test262/built-ins/Temporal/PlainTime
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/tests/test262/built-ins/Temporal/PlainTime')
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/basic.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/builtin.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-cast.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-number.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-calendar-annotation.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-critical-unknown-annotation.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-date-with-utc-offset.js55
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-multiple-calendar.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-multiple-time-zone.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-no-implicit-midnight.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-time-designator-required-for-disambiguation.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-time-separators.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-time-zone-annotation.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-unknown-annotation.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-with-time-designator.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-with-utc-designator.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-wrong-type.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-negative-epochnanoseconds.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/basic.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/builtin.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/exhaustive.js119
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/leap-second.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/not-a-constructor.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/plaintime-propertybag-no-time-units.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/use-internal-slots.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/year-zero.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/constructor.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-number.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-object-leap-second.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-object.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-plaindatetime.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-plaintime.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-calendar-annotation.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-critical-unknown-annotation.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-date-with-utc-offset.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-leap-second.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-multiple-time-zone.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-no-implicit-midnight.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-time-designator-required-for-disambiguation.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-time-separators.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-time-zone-annotation.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-trailing-junk.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-unknown-annotation.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-with-time-designator.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-with-utc-designator.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string.js60
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-wrong-type.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-balance-negative-time-units.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-negative-epochnanoseconds.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/builtin.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/infinity-throws-rangeerror.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/leap-second.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/not-a-constructor.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/observable-get-overflow-argument-string-invalid.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-invalid.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-wrong-type.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/order-of-operations.js55
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-constrain.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-invalid-string.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-reject.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-undefined.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-wrong-type.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/plaintime-propertybag-no-time-units.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/subclassing-ignored.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/year-zero.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/hour-undefined.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/infinity-throws-rangeerror.js60
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/microsecond-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/millisecond-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/minute-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/nanosecond-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/negative-infinity-throws-rangeerror.js60
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/negative-zero.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration-max.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration-out-of-range.js75
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration-precision-exact-numerical-values.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-higher-units.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-invalid-property.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-mixed-sign.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-not-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-object.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-singular-properties.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string-duration-too-large.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string-fractional-units-rounding-mode.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string-negative-fractional-units.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/balance-negative-time-units.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/negative-infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/non-integer-throws-rangeerror.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/options-ignored.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/order-of-operations.js62
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/precision-exact-mathematical-values-1.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/precision-exact-mathematical-values-2.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/constructor.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-cast.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-calendar-annotation.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-critical-unknown-annotation.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-date-with-utc-offset.js52
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-multiple-time-zone.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-no-implicit-midnight.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-time-designator-required-for-disambiguation.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-time-separators.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-time-zone-annotation.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-unknown-annotation.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-with-time-designator.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-with-utc-designator.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-balance-negative-time-units.js44
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-negative-epochnanoseconds.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/basic.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/plaintime-propertybag-no-time-units.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/year-zero.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/field-names.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/field-prop-desc.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/field-traversal-order.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/prototype.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/prop-desc.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/options-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/rounding-cross-midnight.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-hours.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-invalid.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-microseconds.js60
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-milliseconds.js60
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-minutes.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-nan.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-nanoseconds.js60
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-non-integer.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-out-of-range.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-seconds.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-undefined.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-ceil.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-expand.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-floor.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfCeil.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfEven.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfExpand.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfFloor.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfTrunc.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-invalid-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-trunc.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-undefined.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-wrong-type.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundto-invalid-string.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-invalid-string.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-missing.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-plurals-accepted.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-string-shorthand.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-wrong-type.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-cast.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-calendar-annotation.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-critical-unknown-annotation.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-date-with-utc-offset.js53
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-multiple-time-zone.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-no-implicit-midnight.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-time-designator-required-for-disambiguation.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-time-separators.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-time-zone-annotation.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-unknown-annotation.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-with-time-designator.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-with-utc-designator.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-balance-negative-time-units.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-negative-epochnanoseconds.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/balance-negative-time-units.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/basic.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-invalid-string.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-plurals-accepted.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-smallestunit-mismatch.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/leap-second.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-invalid.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-undefined.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/order-of-operations.js95
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/plaintime-propertybag-no-time-units.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/result-sub-second.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/round-cross-unit-boundary.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-hours.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-invalid.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-microseconds.js61
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-milliseconds.js61
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-minutes.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-nan.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-nanoseconds.js61
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-non-integer.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-out-of-range.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-seconds.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-ceil.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-expand.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-floor.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfCeil.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfEven.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfExpand.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfFloor.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfTrunc.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-invalid-string.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-trunc.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-undefined.js93
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-invalid-string.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-plurals-accepted.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-undefined.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/year-zero.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-max.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-out-of-range.js75
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-precision-exact-numerical-values.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-higher-units.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-invalid-property.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-mixed-sign.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-not-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-object.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-singular-properties.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-duration-too-large.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-fractional-units-rounding-mode.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-negative-fractional-units.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/balance-negative-time-units.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/negative-infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/non-integer-throws-rangeerror.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/options-ignored.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/order-of-operations.js62
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/precision-exact-mathematical-values-1.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/precision-exact-mathematical-values-2.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/basic.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/return-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-leap-second.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-plaindatetime.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-case-insensitive.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-leap-second.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-string.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-calendar-annotation.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-critical-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-date-with-utc-offset.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-invalid.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-time-separators.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-time-zone-annotation.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-unknown-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/basic.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-datefromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-fields-iterable.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-temporal-object.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/limits.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/basic.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-auto.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-invalid-string.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-nan.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-non-integer.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-number.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-out-of-range.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-undefined.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-wrong-type.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-invalid.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-object.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-undefined.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/order-of-operations.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/rounding-cross-midnight.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-ceil.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-expand.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-floor.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfCeil.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfEven.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfExpand.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfFloor.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfTrunc.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-invalid-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-trunc.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-wrong-type.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-fractionalseconddigits.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-invalid-string.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-plurals-accepted.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-undefined.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-valid-units.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-wrong-type.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toStringTag/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toStringTag/prop-desc.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toStringTag/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-plaindatetime.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-primitive.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-case-insensitive.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-leap-second.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-number.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-missing-properties.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-calendar-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-critical-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-date-with-utc-offset.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-invalid.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-time-separators.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-time-zone-annotation.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/basic.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-datefromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-fields-iterable.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-temporal-object.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/getpossibleinstantsfor-called-with-iso8601-calendar.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/order-of-operations.js118
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/plaindate-infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-case-insensitive.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-not-callable.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-datetime.js65
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-leap-second.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-multiple-offsets.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-year-zero.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-wrong-type.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-cast.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-calendar-annotation.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-critical-unknown-annotation.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-date-with-utc-offset.js53
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-multiple-time-zone.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-no-implicit-midnight.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-time-designator-required-for-disambiguation.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-time-separators.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-time-zone-annotation.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-unknown-annotation.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-with-time-designator.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-with-utc-designator.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-balance-negative-time-units.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-negative-epochnanoseconds.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/balance-negative-time-units.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/basic.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-invalid-string.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-plurals-accepted.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-smallestunit-mismatch.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/leap-second.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/options-invalid.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/options-undefined.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/order-of-operations.js95
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/plaintime-propertybag-no-time-units.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/result-sub-second.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/round-cross-unit-boundary.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-hours.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-invalid.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-microseconds.js61
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-milliseconds.js61
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-minutes.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-nan.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-nanoseconds.js61
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-non-integer.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-out-of-range.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-seconds.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-ceil.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-expand.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-floor.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfCeil.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfEven.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfExpand.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfFloor.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfTrunc.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-invalid-string.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-trunc.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-undefined.js93
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/smallestunit-invalid-string.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/smallestunit-plurals-accepted.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/smallestunit-undefined.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/smallestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/year-zero.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/basic.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/argument-not-object.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/basic.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/copy-properties-not-undefined.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/infinity-throws-rangeerror.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-invalid.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-undefined.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/order-of-operations.js59
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/overflow-invalid-string.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/overflow-undefined.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/overflow-wrong-type.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/plaintimelike-invalid.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/second-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/subclass.js21
628 files changed, 18205 insertions, 0 deletions
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/basic.js
new file mode 100644
index 0000000000..fad1f11b39
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/basic.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime
+description: Basic tests for the PlainTime constructor.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const args = [15, 23, 30, 123, 456, 789];
+const plainTime = new Temporal.PlainTime(...args);
+TemporalHelpers.assertPlainTime(plainTime, ...args);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/builtin.js
new file mode 100644
index 0000000000..26de31a9aa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/builtin.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime
+description: Tests that Temporal.PlainTime meets the requirements for built-in objects
+info: |
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(typeof Temporal.PlainTime.prototype,
+ "object", "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-cast.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-cast.js
new file mode 100644
index 0000000000..d253be6629
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-cast.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: compare() casts its arguments
+features: [Temporal]
+---*/
+
+const t1 = Temporal.PlainTime.from("08:44:15.321");
+const t2 = Temporal.PlainTime.from("14:23:30.123");
+
+assert.sameValue(Temporal.PlainTime.compare({ hour: 16, minute: 34 }, t2), 1, "one object");
+assert.sameValue(Temporal.PlainTime.compare("16:34", t2), 1, "one string");
+assert.throws(TypeError, () => Temporal.PlainTime.compare({ hours: 16 }, t2), "one missing property");
+
+assert.sameValue(Temporal.PlainTime.compare(t1, { hour: 16, minute: 34 }), -1, "two object");
+assert.sameValue(Temporal.PlainTime.compare(t1, "16:34"), -1, "two string");
+assert.throws(TypeError, () => Temporal.PlainTime.compare(t1, { hours: 16 }), "two missing property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-number.js
new file mode 100644
index 0000000000..91e29a6769
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-number.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: A number is invalid in place of an ISO string for Temporal.PlainTime
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ -123456.987654321,
+ 1234567,
+ 123456.9876543219,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainTime.compare(arg, new Temporal.PlainTime(12, 34, 56, 987, 654, 321)),
+ `A number (${arg}) is not a valid ISO string for PlainTime (first argument)`
+ );
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainTime.compare(new Temporal.PlainTime(12, 34, 56, 987, 654, 321), arg),
+ `A number (${arg}) is not a valid ISO string for PlainTime (second argument)`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..7d2b108223
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-calendar-annotation.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["12:34:56.987654321[u-ca=iso8601]", "without time zone"],
+ ["12:34:56.987654321[UTC][u-ca=iso8601]", "with time zone"],
+ ["12:34:56.987654321[!u-ca=iso8601]", "with ! and no time zone"],
+ ["12:34:56.987654321[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["T12:34:56.987654321[u-ca=iso8601]", "with T and no time zone"],
+ ["T12:34:56.987654321[UTC][u-ca=iso8601]", "with T and time zone"],
+ ["T12:34:56.987654321[!u-ca=iso8601]", "with T, !, and no time zone"],
+ ["T12:34:56.987654321[UTC][!u-ca=iso8601]", "with T, !, and time zone"],
+ ["1970-01-01T12:34:56.987654321[u-ca=iso8601]", "with date and no time zone"],
+ ["1970-01-01T12:34:56.987654321[UTC][u-ca=iso8601]", "with date and time zone"],
+ ["1970-01-01T12:34:56.987654321[!u-ca=iso8601]", "with !, date, and no time zone"],
+ ["1970-01-01T12:34:56.987654321[UTC][!u-ca=iso8601]", "with !, date, and time zone"],
+ ["1970-01-01T12:34:56.987654321[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainTime.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..d553e57b96
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "00:00[!foo=bar]",
+ "T00:00[!foo=bar]",
+ "1970-01-01T00:00[!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(arg, new Temporal.PlainTime(12, 34, 56, 987, 654, 321)),
+ `reject unknown annotation with critical flag: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(new Temporal.PlainTime(12, 34, 56, 987, 654, 321), arg),
+ `reject unknown annotation with critical flag: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..08ceab76c6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-date-with-utc-offset.js
@@ -0,0 +1,55 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const validStrings = [
+ "12:34:56.987654321+00:00",
+ "12:34:56.987654321+00:00[UTC]",
+ "12:34:56.987654321+00:00[!UTC]",
+ "12:34:56.987654321-02:30[America/St_Johns]",
+ "1976-11-18T12:34:56.987654321+00:00",
+ "1976-11-18T12:34:56.987654321+00:00[UTC]",
+ "1976-11-18T12:34:56.987654321+00:00[!UTC]",
+ "1976-11-18T12:34:56.987654321-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = Temporal.PlainTime.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `"${arg}" is a valid UTC offset with time for PlainTime`
+ );
+}
+
+const invalidStrings = [
+ "2022-09-15Z",
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(arg, new Temporal.PlainTime(12, 34, 56, 987, 654, 321)),
+ `"${arg}" UTC offset without time is not valid for PlainTime (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(new Temporal.PlainTime(12, 34, 56, 987, 654, 321), arg),
+ `"${arg}" UTC offset without time is not valid for PlainTime (second argument)`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..5ce6745e94
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-multiple-calendar.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(arg, new Temporal.PlainTime(12, 34, 56, 987, 654, 321)),
+ `reject more than one calendar annotation if any critical: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(new Temporal.PlainTime(12, 34, 56, 987, 654, 321), arg),
+ `reject more than one calendar annotation if any critical: ${arg} (first argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..2343613c5a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-multiple-time-zone.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "00:00[UTC][UTC]",
+ "T00:00[UTC][UTC]",
+ "1970-01-01T00:00[UTC][UTC]",
+ "1970-01-01T00:00[!UTC][UTC]",
+ "1970-01-01T00:00[UTC][!UTC]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00[UTC][foo=bar][UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(arg, new Temporal.PlainTime(12, 34, 56, 987, 654, 321)),
+ `reject more than one time zone annotation: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(new Temporal.PlainTime(12, 34, 56, 987, 654, 321), arg),
+ `reject more than one time zone annotation: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-no-implicit-midnight.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-no-implicit-midnight.js
new file mode 100644
index 0000000000..67e51e9a45
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-no-implicit-midnight.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: RangeError thrown if a date-only string is passed in a PlainTime context
+features: [Temporal, arrow-function]
+---*/
+
+const arg = "2019-10-01";
+const midnight = new Temporal.PlainTime();
+assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(arg, midnight),
+ "Date-only string throws, does not implicitly convert to midnight (first argument)"
+);
+assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(midnight, arg),
+ "Date-only string throws, does not implicitly convert to midnight (second argument)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-time-designator-required-for-disambiguation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-time-designator-required-for-disambiguation.js
new file mode 100644
index 0000000000..d6a099db5e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-time-designator-required-for-disambiguation.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: ISO 8601 time designator "T" required in cases of ambiguity
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const midnight = new Temporal.PlainTime();
+
+TemporalHelpers.ISO.plainTimeStringsAmbiguous().forEach((string) => {
+ let arg = string;
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(arg, midnight),
+ `'${arg}' is ambiguous and requires T prefix (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(midnight, arg),
+ `'${arg}' is ambiguous and requires T prefix (second argument)`
+ );
+ // The same string with a T prefix should not throw:
+ arg = `T${string}`;
+ Temporal.PlainTime.compare(arg, midnight);
+ Temporal.PlainTime.compare(midnight, arg);
+
+ arg = ` ${string}`;
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(arg, midnight),
+ `space is not accepted as a substitute for T prefix (first argument): '${arg}'`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(midnight, arg),
+ `space is not accepted as a substitute for T prefix (second argument): '${arg}'`
+ );
+});
+
+// None of these should throw without a T prefix, because they are unambiguously time strings:
+TemporalHelpers.ISO.plainTimeStringsUnambiguous().forEach((arg) => {
+ Temporal.PlainTime.compare(arg, midnight);
+ Temporal.PlainTime.compare(midnight, arg);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-time-separators.js
new file mode 100644
index 0000000000..d98f91a43a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-time-separators.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const tests = [
+ ["1976-11-18T12:34:56.987654321", "uppercase T"],
+ ["1976-11-18t12:34:56.987654321", "lowercase T"],
+ ["1976-11-18 12:34:56.987654321", "space between date and time"],
+ ["T12:34:56.987654321", "time-only uppercase T"],
+ ["t12:34:56.987654321", "time-only lowercase T"],
+];
+
+tests.forEach(([arg, description]) => {
+ assert.sameValue(
+ Temporal.PlainTime.compare(arg, plainTime),
+ 0,
+ `variant time separators (${description}), first argument`
+ );
+
+ assert.sameValue(
+ Temporal.PlainTime.compare(plainTime, arg),
+ 0,
+ `variant time separators (${description}), second argument`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..272daa9d91
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-time-zone-annotation.js
@@ -0,0 +1,48 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["12:34:56.987654321[Asia/Kolkata]", "named, with no offset"],
+ ["12:34:56.987654321[!Europe/Vienna]", "named, with ! and no offset"],
+ ["12:34:56.987654321[+00:00]", "numeric, with no offset"],
+ ["12:34:56.987654321[!-02:30]", "numeric, with ! and no offset"],
+ ["T12:34:56.987654321[UTC]", "named, with T and no offset"],
+ ["T12:34:56.987654321[!Africa/Abidjan]", "named, with T, !, and no offset"],
+ ["T12:34:56.987654321[+01:00]", "numeric, with T and no offset"],
+ ["T12:34:56.987654321[!-08:00]", "numeric, with T, !, and no offset"],
+ ["12:34:56.987654321+00:00[America/Sao_Paulo]", "named, with offset"],
+ ["12:34:56.987654321+00:00[!Asia/Tokyo]", "named, with ! and offset"],
+ ["12:34:56.987654321+00:00[-02:30]", "numeric, with offset"],
+ ["12:34:56.987654321+00:00[!+00:00]", "numeric, with ! and offset"],
+ ["T12:34:56.987654321+00:00[America/New_York]", "named, with T and offset"],
+ ["T12:34:56.987654321+00:00[!UTC]", "named, with T, !, and offset"],
+ ["T12:34:56.987654321+00:00[-08:00]", "numeric, with T and offset"],
+ ["T12:34:56.987654321+00:00[!+01:00]", "numeric, with T, !, and offset"],
+ ["1970-01-01T12:34:56.987654321[Africa/Lagos]", "named, with date and no offset"],
+ ["1970-01-01T12:34:56.987654321[!America/Vancouver]", "named, with date, !, and no offset"],
+ ["1970-01-01T12:34:56.987654321[+00:00]", "numeric, with date and no offset"],
+ ["1970-01-01T12:34:56.987654321[!-02:30]", "numeric, with date, !, and no offset"],
+ ["1970-01-01T12:34:56.987654321+00:00[Europe/London]", "named, with date and offset"],
+ ["1970-01-01T12:34:56.987654321+00:00[!Asia/Seoul]", "named, with date, offset, and !"],
+ ["1970-01-01T12:34:56.987654321+00:00[+01:00]", "numeric, with date and offset"],
+ ["1970-01-01T12:34:56.987654321+00:00[!-08:00]", "numeric, with date, offset, and !"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainTime.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..ddfba8993f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-unknown-annotation.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["12:34:56.987654321[foo=bar]", "alone"],
+ ["12:34:56.987654321[UTC][foo=bar]", "with time zone"],
+ ["12:34:56.987654321[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["T12:34:56.987654321[foo=bar]", "with T"],
+ ["T12:34:56.987654321[UTC][foo=bar]", "with T and time zone"],
+ ["T12:34:56.987654321[u-ca=iso8601][foo=bar]", "with T and calendar"],
+ ["T12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with T, time zone, and calendar"],
+ ["1970-01-01T12:34:56.987654321[foo=bar]", "with date"],
+ ["1970-01-01T12:34:56.987654321[UTC][foo=bar]", "with date and time zone"],
+ ["1970-01-01T12:34:56.987654321[u-ca=iso8601][foo=bar]", "with date and calendar"],
+ ["1970-01-01T12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with date, time zone, and calendar"],
+ ["1970-01-01T12:34:56.987654321[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainTime.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-with-time-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-with-time-designator.js
new file mode 100644
index 0000000000..3bfde29db9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-with-time-designator.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: ISO 8601 time designator "T" allowed at the start of PlainTime strings
+features: [Temporal, arrow-function]
+---*/
+
+const halfPast = new Temporal.PlainTime(0, 30);
+const validStrings = [
+ "T00:30",
+ "t00:30",
+ "T0030",
+ "t0030",
+ "T00:30:00",
+ "t00:30:00",
+ "T003000",
+ "t003000",
+ "T00:30:00.000000000",
+ "t00:30:00.000000000",
+ "T003000.000000000",
+ "t003000.000000000",
+];
+validStrings.forEach((arg) => {
+ assert.sameValue(Temporal.PlainTime.compare(arg, halfPast), 0, `T prefix is accepted: ${arg} (first argument)`);
+ assert.sameValue(Temporal.PlainTime.compare(halfPast, arg), 0, `T prefix is accepted: ${arg} (second argument)`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..abff07e6ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-with-utc-designator.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: RangeError thrown if a string with UTC designator is used as a PlainTime
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+ "09:00:00Z[UTC]",
+ "09:00:00Z",
+];
+const plainTime = new Temporal.PlainTime();
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(arg, plainTime),
+ "String with UTC designator should not be valid as a PlainTime (first argument)"
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(plainTime, arg),
+ "String with UTC designator should not be valid as a PlainTime (second argument)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-wrong-type.js
new file mode 100644
index 0000000000..225a8d16c9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-wrong-type.js
@@ -0,0 +1,47 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainTime
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => Temporal.PlainTime.compare(arg, new Temporal.PlainTime(12, 34, 56, 987, 654, 321)),
+ `${description} does not convert to a valid ISO string (first argument)`
+ );
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => Temporal.PlainTime.compare(new Temporal.PlainTime(12, 34, 56, 987, 654, 321), arg),
+ `${description} does not convert to a valid ISO string (second argument)`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainTime, "Temporal.PlainTime, object"],
+ [Temporal.PlainTime.prototype, "Temporal.PlainTime.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.PlainTime.compare(arg, new Temporal.PlainTime(12, 34, 56, 987, 654, 321)), `${description} is not a valid property bag and does not convert to a string (first argument)`);
+ assert.throws(TypeError, () => Temporal.PlainTime.compare(new Temporal.PlainTime(12, 34, 56, 987, 654, 321), arg), `${description} is not a valid property bag and does not convert to a string (second argument)`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..55145852e3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+const time = new Temporal.PlainTime(16, 50, 35, 0, 0, 1);
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const result1 = Temporal.PlainTime.compare(time, datetime);
+assert.sameValue(result1, 0);
+
+const result2 = Temporal.PlainTime.compare(datetime, time);
+assert.sameValue(result2, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..13008889fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, Infinity, -Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+ assert.throws(RangeError, () => Temporal.PlainTime.compare(datetime, time));
+ assert.throws(RangeError, () => Temporal.PlainTime.compare(time, datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..c16b088148
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainTime.compare(datetime, time),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainTime.compare(time, datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..53767252b9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+ assert.throws(RangeError, () => Temporal.PlainTime.compare(datetime, time));
+ assert.throws(RangeError, () => Temporal.PlainTime.compare(time, datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..22209b6693
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+ assert.throws(TypeError, () => Temporal.PlainTime.compare(datetime, time));
+ assert.throws(TypeError, () => Temporal.PlainTime.compare(time, datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/basic.js
new file mode 100644
index 0000000000..212dc43f85
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/basic.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: Basic tests for compare()
+features: [Temporal]
+---*/
+
+const t1 = Temporal.PlainTime.from("08:44:15.321");
+const t1bis = Temporal.PlainTime.from("08:44:15.321");
+const t2 = Temporal.PlainTime.from("14:23:30.123");
+
+assert.sameValue(Temporal.PlainTime.compare(t1, t1), 0, "same object");
+assert.sameValue(Temporal.PlainTime.compare(t1, t1bis), 0, "different object");
+assert.sameValue(Temporal.PlainTime.compare(t1, t2), -1, "before");
+assert.sameValue(Temporal.PlainTime.compare(t2, t1), 1, "after");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/builtin.js
new file mode 100644
index 0000000000..3d5821934a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/builtin.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: Tests that Temporal.PlainTime.compare meets the requirements for built-in objects
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainTime.compare),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.compare),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.compare),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.compare.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/exhaustive.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/exhaustive.js
new file mode 100644
index 0000000000..d711fce237
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/exhaustive.js
@@ -0,0 +1,119 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: Tests for compare() with each possible outcome
+features: [Temporal]
+---*/
+
+const cal1 = "iso8601";
+const cal2 = new (class extends Temporal.Calendar { id = "custom"; })("iso8601");
+
+assert.sameValue(
+ Temporal.PlainTime.compare(
+ new Temporal.PlainTime(12, 15, 45, 333, 777, 111, cal1),
+ new Temporal.PlainTime(6, 15, 45, 333, 777, 111, cal2)
+ ),
+ 1,
+ "hour >"
+);
+assert.sameValue(
+ Temporal.PlainTime.compare(
+ new Temporal.PlainTime(6, 30, 15, 222, 444, 6, cal1),
+ new Temporal.PlainTime(22, 30, 15, 222, 444, 6, cal2)
+ ),
+ -1,
+ "hour <"
+);
+assert.sameValue(
+ Temporal.PlainTime.compare(
+ new Temporal.PlainTime(12, 45, 15, 333, 777, 111, cal1),
+ new Temporal.PlainTime(12, 15, 22, 333, 777, 111, cal2)
+ ),
+ 1,
+ "minute >"
+);
+assert.sameValue(
+ Temporal.PlainTime.compare(
+ new Temporal.PlainTime(6, 30, 15, 222, 444, 6, cal1),
+ new Temporal.PlainTime(6, 57, 15, 222, 444, 6, cal2)
+ ),
+ -1,
+ "minute <"
+);
+assert.sameValue(
+ Temporal.PlainTime.compare(
+ new Temporal.PlainTime(12, 15, 6, 333, 777, 111, cal1),
+ new Temporal.PlainTime(12, 15, 5, 333, 777, 111, cal2)
+ ),
+ 1,
+ "second >"
+);
+assert.sameValue(
+ Temporal.PlainTime.compare(
+ new Temporal.PlainTime(6, 30, 3, 222, 444, 6, cal1),
+ new Temporal.PlainTime(6, 30, 4, 222, 444, 6, cal2)
+ ),
+ -1,
+ "second <"
+);
+assert.sameValue(
+ Temporal.PlainTime.compare(
+ new Temporal.PlainTime(12, 15, 45, 6, 777, 111, cal1),
+ new Temporal.PlainTime(12, 15, 45, 5, 777, 111, cal2)
+ ),
+ 1,
+ "millisecond >"
+);
+assert.sameValue(
+ Temporal.PlainTime.compare(
+ new Temporal.PlainTime(6, 30, 15, 3, 444, 6, cal1),
+ new Temporal.PlainTime(6, 30, 15, 4, 444, 6, cal2)
+ ),
+ -1,
+ "millisecond <"
+);
+assert.sameValue(
+ Temporal.PlainTime.compare(
+ new Temporal.PlainTime(12, 15, 45, 333, 6, 111, cal1),
+ new Temporal.PlainTime(12, 15, 45, 333, 5, 111, cal2)
+ ),
+ 1,
+ "microsecond >"
+);
+assert.sameValue(
+ Temporal.PlainTime.compare(
+ new Temporal.PlainTime(6, 30, 15, 222, 3, 6, cal1),
+ new Temporal.PlainTime(6, 30, 15, 222, 4, 6, cal2)
+ ),
+ -1,
+ "microsecond <"
+);
+assert.sameValue(
+ Temporal.PlainTime.compare(
+ new Temporal.PlainTime(12, 15, 45, 333, 777, 999, cal1),
+ new Temporal.PlainTime(12, 15, 45, 333, 777, 111, cal2)
+ ),
+ 1,
+ "nanosecond >"
+);
+assert.sameValue(
+ Temporal.PlainTime.compare(
+ new Temporal.PlainTime(6, 30, 15, 222, 444, 0, cal1),
+ new Temporal.PlainTime(6, 30, 15, 222, 444, 6, cal2)
+ ),
+ -1,
+ "nanosecond <"
+);
+assert.sameValue(
+ Temporal.PlainTime.compare(
+ new Temporal.PlainTime(12, 15, 45, 333, 777, 111, cal1),
+ new Temporal.PlainTime(12, 15, 45, 333, 777, 111, cal2)
+ ),
+ 0,
+ "="
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/leap-second.js
new file mode 100644
index 0000000000..d9d9b320dd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/leap-second.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: Leap second is a valid ISO string for PlainTime
+features: [Temporal]
+---*/
+
+let arg = "2016-12-31T23:59:60";
+const result1 = Temporal.PlainTime.compare(arg, new Temporal.PlainTime(23, 59, 59));
+assert.sameValue(result1, 0, "leap second is a valid ISO string for PlainTime (first argument)");
+const result2 = Temporal.PlainTime.compare(new Temporal.PlainTime(23, 59, 59), arg);
+assert.sameValue(result2, 0, "leap second is a valid ISO string for PlainTime (first argument)");
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result3 = Temporal.PlainTime.compare(arg, new Temporal.PlainTime(23, 59, 59));
+assert.sameValue(result3, 0, "second: 60 is ignored in property bag for PlainTime (first argument)");
+const result4 = Temporal.PlainTime.compare(new Temporal.PlainTime(23, 59, 59), arg);
+assert.sameValue(result4, 0, "second: 60 is ignored in property bag for PlainTime (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/length.js
new file mode 100644
index 0000000000..e917ed6a97
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: Temporal.PlainTime.compare.length is 2
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.compare, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/name.js
new file mode 100644
index 0000000000..ab741acddb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: Temporal.PlainTime.compare.name is "compare"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.compare, "name", {
+ value: "compare",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/not-a-constructor.js
new file mode 100644
index 0000000000..849f81c831
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/not-a-constructor.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: Temporal.PlainTime.compare does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainTime.compare();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.compare), false,
+ "isConstructor(Temporal.PlainTime.compare)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/plaintime-propertybag-no-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/plaintime-propertybag-no-time-units.js
new file mode 100644
index 0000000000..24afb94488
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/plaintime-propertybag-no-time-units.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: Missing time units in property bag default to 0
+features: [Temporal]
+---*/
+
+const props = {};
+assert.throws(TypeError, () => Temporal.PlainTime.compare(props, new Temporal.PlainTime(0, 30)), "TypeError if no properties are present");
+
+props.minute = 30;
+const result = Temporal.PlainTime.compare(props, new Temporal.PlainTime(0, 30));
+assert.sameValue(result, 0, "missing time units default to 0");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/prop-desc.js
new file mode 100644
index 0000000000..03225cf214
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: The "compare" property of Temporal.PlainTime
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.compare,
+ "function",
+ "`typeof PlainTime.compare` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime, "compare", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/use-internal-slots.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/use-internal-slots.js
new file mode 100644
index 0000000000..67cee1bbca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/use-internal-slots.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-comparetemporaltime
+description: compare() ignores the observable properties and uses internal slots
+features: [Temporal]
+---*/
+
+function CustomError() {}
+
+class AvoidGettersTime extends Temporal.PlainTime {
+ get hour() {
+ throw new CustomError();
+ }
+ get minute() {
+ throw new CustomError();
+ }
+ get second() {
+ throw new CustomError();
+ }
+ get millisecond() {
+ throw new CustomError();
+ }
+ get microsecond() {
+ throw new CustomError();
+ }
+ get nanosecond() {
+ throw new CustomError();
+ }
+}
+
+const one = new AvoidGettersTime(12, 34, 56, 987, 654, 321);
+const two = new AvoidGettersTime(6, 54, 32, 123, 456, 789);
+assert.sameValue(Temporal.PlainTime.compare(one, two), 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/year-zero.js
new file mode 100644
index 0000000000..5ec802a047
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/year-zero.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.compare
+description: Negative zero, as an extended year, fails
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const invalidStrings = [
+ "-000000-12-07T03:24:30",
+ "-000000-12-07T03:24:30+01:00",
+ "-000000-12-07T03:24:30+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(arg, time),
+ "Cannot use minus zero as extended year (first argument)"
+ );
+
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(time, arg),
+ "Cannot use minus zero as extended year (second argument)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/constructor.js
new file mode 100644
index 0000000000..85b3df5864
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/constructor.js
@@ -0,0 +1,15 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime
+description: Temporal.PlainTime constructor cannot be called as a function
+info: |
+ 1. If NewTarget is undefined, throw a TypeError exception.
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => Temporal.PlainTime(12, 0, 0));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-number.js
new file mode 100644
index 0000000000..38a4faf253
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-number.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: A number is invalid in place of an ISO string for Temporal.PlainTime
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ -123456.987654321,
+ 1234567,
+ 123456.9876543219,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainTime.from(arg),
+ `A number (${arg}) is not a valid ISO string for PlainTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-object-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-object-leap-second.js
new file mode 100644
index 0000000000..edf7e02e9b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-object-leap-second.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: Object argument handles leap seconds according to the overflow option.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const options of [undefined, {}, { overflow: "constrain" }]) {
+ TemporalHelpers.assertPlainTime(Temporal.PlainTime.from({ hour: 23, minute: 59, second: 60 }, options),
+ 23, 59, 59, 0, 0, 0);
+ TemporalHelpers.assertPlainTime(Temporal.PlainTime.from({ hour: 12, minute: 30, second: 60 }, options),
+ 12, 30, 59, 0, 0, 0);
+ TemporalHelpers.assertPlainTime(Temporal.PlainTime.from({ hour: 23, minute: 59, second: 60, millisecond: 170 }, options),
+ 23, 59, 59, 170, 0, 0);
+}
+
+const options = { overflow: "reject" };
+assert.throws(RangeError, () => Temporal.PlainTime.from({ hour: 23, minute: 59, second: 60 }, options));
+assert.throws(RangeError, () => Temporal.PlainTime.from({ hour: 12, minute: 30, second: 60 }, options));
+assert.throws(RangeError, () => Temporal.PlainTime.from({ hour: 23, minute: 59, second: 60, millisecond: 170 }, options));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-object.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-object.js
new file mode 100644
index 0000000000..56a7af892e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-object.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: Plain object argument is supported and ignores plural properties
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from({ hour: 15, minute: 23 }),
+ 15, 23, 0, 0, 0, 0);
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from({ minute: 30, microsecond: 555 }),
+ 0, 30, 0, 0, 555, 0);
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from({ year: 2019, month: 10, day: 1, hour: 14, minute: 20, second: 36 }),
+ 14, 20, 36, 0, 0, 0);
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from({ hours: 2, minute: 30, microsecond: 555 }),
+ 0, 30, 0, 0, 555, 0);
+
+assert.throws(TypeError, () => Temporal.PlainTime.from({}));
+assert.throws(TypeError, () => Temporal.PlainTime.from({ minutes: 12 }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-plaindatetime.js
new file mode 100644
index 0000000000..920c3150ac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-plaindatetime.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainTime by reading internal slots
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((plainDateTime) => {
+ const result = Temporal.PlainTime.from(plainDateTime);
+ TemporalHelpers.assertPlainTime(result, 12, 34, 56, 987, 654, 321);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-plaintime.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-plaintime.js
new file mode 100644
index 0000000000..33fe44b8d0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-plaintime.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: A PlainTime object is copied, not returned directly
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const orig = new Temporal.PlainTime(11, 42, 0, 0, 0, 0);
+const result = Temporal.PlainTime.from(orig);
+
+TemporalHelpers.assertPlainTime(
+ result,
+ 11, 42, 0, 0, 0, 0,
+ "PlainTime is copied"
+);
+
+assert.notSameValue(
+ result,
+ orig,
+ "When a PlainTime is given, the returned value is not the original PlainTime"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..917b78d3ed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-calendar-annotation.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["12:34:56.987654321[u-ca=iso8601]", "without time zone"],
+ ["12:34:56.987654321[UTC][u-ca=iso8601]", "with time zone"],
+ ["12:34:56.987654321[!u-ca=iso8601]", "with ! and no time zone"],
+ ["12:34:56.987654321[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["T12:34:56.987654321[u-ca=iso8601]", "with T and no time zone"],
+ ["T12:34:56.987654321[UTC][u-ca=iso8601]", "with T and time zone"],
+ ["T12:34:56.987654321[!u-ca=iso8601]", "with T, !, and no time zone"],
+ ["T12:34:56.987654321[UTC][!u-ca=iso8601]", "with T, !, and time zone"],
+ ["1970-01-01T12:34:56.987654321[u-ca=iso8601]", "with date and no time zone"],
+ ["1970-01-01T12:34:56.987654321[UTC][u-ca=iso8601]", "with date and time zone"],
+ ["1970-01-01T12:34:56.987654321[!u-ca=iso8601]", "with !, date, and no time zone"],
+ ["1970-01-01T12:34:56.987654321[UTC][!u-ca=iso8601]", "with !, date, and time zone"],
+ ["12:34:56.987654321[u-ca=hebrew]", "calendar annotation ignored"],
+ ["12:34:56.987654321[u-ca=unknown]", "calendar annotation ignored even if unknown calendar"],
+ ["12:34:56.987654321[!u-ca=unknown]", "calendar annotation ignored even if unknown calendar with !"],
+ ["1970-01-01T12:34:56.987654321[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainTime.from(arg);
+
+ TemporalHelpers.assertPlainTime(
+ result,
+ 12, 34, 56, 987, 654, 321,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..ed8825b591
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "00:00[!foo=bar]",
+ "T00:00[!foo=bar]",
+ "1970-01-01T00:00[!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.from(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..d715e51791
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-date-with-utc-offset.js
@@ -0,0 +1,51 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const validStrings = [
+ "12:34:56.987654321+00:00",
+ "12:34:56.987654321+00:00[UTC]",
+ "12:34:56.987654321+00:00[!UTC]",
+ "12:34:56.987654321-02:30[America/St_Johns]",
+ "1976-11-18T12:34:56.987654321+00:00",
+ "1976-11-18T12:34:56.987654321+00:00[UTC]",
+ "1976-11-18T12:34:56.987654321+00:00[!UTC]",
+ "1976-11-18T12:34:56.987654321-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = Temporal.PlainTime.from(arg);
+
+ TemporalHelpers.assertPlainTime(
+ result,
+ 12, 34, 56, 987, 654, 321,
+ `"${arg}" is a valid UTC offset with time for PlainTime`
+ );
+}
+
+const invalidStrings = [
+ "2022-09-15Z",
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.from(arg),
+ `"${arg}" UTC offset without time is not valid for PlainTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-leap-second.js
new file mode 100644
index 0000000000..ef9582da8b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-leap-second.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: Leap second is replaced by :59 in ISO strings.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const options of [undefined, {}, { overflow: "constrain" }, { overflow: "reject" }]) {
+ TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("23:59:60", options),
+ 23, 59, 59, 0, 0, 0);
+ TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("12:30:60", options),
+ 12, 30, 59, 0, 0, 0);
+ TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("23:59:60.170", options),
+ 23, 59, 59, 170, 0, 0);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..3e3f964b70
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-multiple-calendar.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.from(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..6ed71fbbc4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-multiple-time-zone.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "00:00[UTC][UTC]",
+ "T00:00[UTC][UTC]",
+ "1970-01-01T00:00[UTC][UTC]",
+ "1970-01-01T00:00[!UTC][UTC]",
+ "1970-01-01T00:00[UTC][!UTC]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00[UTC][foo=bar][UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.from(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-no-implicit-midnight.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-no-implicit-midnight.js
new file mode 100644
index 0000000000..3555c8f350
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-no-implicit-midnight.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: RangeError thrown if a date-only string is passed in a PlainTime context
+features: [Temporal, arrow-function]
+---*/
+
+const arg = "2019-10-01";
+assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.from(arg),
+ "Date-only string throws, does not implicitly convert to midnight"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-time-designator-required-for-disambiguation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-time-designator-required-for-disambiguation.js
new file mode 100644
index 0000000000..30a22c3072
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-time-designator-required-for-disambiguation.js
@@ -0,0 +1,35 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: ISO 8601 time designator "T" required in cases of ambiguity
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+TemporalHelpers.ISO.plainTimeStringsAmbiguous().forEach((string) => {
+ let arg = string;
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.from(arg),
+ `'${arg}' is ambiguous and requires T prefix`
+ );
+ // The same string with a T prefix should not throw:
+ arg = `T${string}`;
+ Temporal.PlainTime.from(arg);
+
+ arg = ` ${string}`;
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.from(arg),
+ `space is not accepted as a substitute for T prefix: '${arg}'`
+ );
+});
+
+// None of these should throw without a T prefix, because they are unambiguously time strings:
+TemporalHelpers.ISO.plainTimeStringsUnambiguous().forEach(
+ (arg) => Temporal.PlainTime.from(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-time-separators.js
new file mode 100644
index 0000000000..2e2645a48e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-time-separators.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T12:34:56.987654321", "uppercase T"],
+ ["1976-11-18t12:34:56.987654321", "lowercase T"],
+ ["1976-11-18 12:34:56.987654321", "space between date and time"],
+ ["T12:34:56.987654321", "time-only uppercase T"],
+ ["t12:34:56.987654321", "time-only lowercase T"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainTime.from(arg);
+
+ TemporalHelpers.assertPlainTime(
+ result,
+ 12, 34, 56, 987, 654, 321,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..6459e18647
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-time-zone-annotation.js
@@ -0,0 +1,49 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["12:34:56.987654321[Asia/Kolkata]", "named, with no offset"],
+ ["12:34:56.987654321[!Europe/Vienna]", "named, with ! and no offset"],
+ ["12:34:56.987654321[+00:00]", "numeric, with no offset"],
+ ["12:34:56.987654321[!-02:30]", "numeric, with ! and no offset"],
+ ["T12:34:56.987654321[UTC]", "named, with T and no offset"],
+ ["T12:34:56.987654321[!Africa/Abidjan]", "named, with T, !, and no offset"],
+ ["T12:34:56.987654321[+01:00]", "numeric, with T and no offset"],
+ ["T12:34:56.987654321[!-08:00]", "numeric, with T, !, and no offset"],
+ ["12:34:56.987654321+00:00[America/Sao_Paulo]", "named, with offset"],
+ ["12:34:56.987654321+00:00[!Asia/Tokyo]", "named, with ! and offset"],
+ ["12:34:56.987654321+00:00[-02:30]", "numeric, with offset"],
+ ["12:34:56.987654321+00:00[!+00:00]", "numeric, with ! and offset"],
+ ["T12:34:56.987654321+00:00[America/New_York]", "named, with T and offset"],
+ ["T12:34:56.987654321+00:00[!UTC]", "named, with T, !, and offset"],
+ ["T12:34:56.987654321+00:00[-08:00]", "numeric, with T and offset"],
+ ["T12:34:56.987654321+00:00[!+01:00]", "numeric, with T, !, and offset"],
+ ["1970-01-01T12:34:56.987654321[Africa/Lagos]", "named, with date and no offset"],
+ ["1970-01-01T12:34:56.987654321[!America/Vancouver]", "named, with date, !, and no offset"],
+ ["1970-01-01T12:34:56.987654321[+00:00]", "numeric, with date and no offset"],
+ ["1970-01-01T12:34:56.987654321[!-02:30]", "numeric, with date, !, and no offset"],
+ ["1970-01-01T12:34:56.987654321+00:00[Europe/London]", "named, with date and offset"],
+ ["1970-01-01T12:34:56.987654321+00:00[!Asia/Seoul]", "named, with date, offset, and !"],
+ ["1970-01-01T12:34:56.987654321+00:00[+01:00]", "numeric, with date and offset"],
+ ["1970-01-01T12:34:56.987654321+00:00[!-08:00]", "numeric, with date, offset, and !"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainTime.from(arg);
+
+ TemporalHelpers.assertPlainTime(
+ result,
+ 12, 34, 56, 987, 654, 321,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-trailing-junk.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-trailing-junk.js
new file mode 100644
index 0000000000..782b168939
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-trailing-junk.js
@@ -0,0 +1,14 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: RangeError thrown if a string argument has trailing junk
+features: [Temporal, arrow-function]
+---*/
+
+const arg = "15:23:30.100junk";
+assert.throws(RangeError, () => Temporal.PlainTime.from(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..7af06b9583
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-unknown-annotation.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["12:34:56.987654321[foo=bar]", "alone"],
+ ["12:34:56.987654321[UTC][foo=bar]", "with time zone"],
+ ["12:34:56.987654321[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["T12:34:56.987654321[foo=bar]", "with T"],
+ ["T12:34:56.987654321[UTC][foo=bar]", "with T and time zone"],
+ ["T12:34:56.987654321[u-ca=iso8601][foo=bar]", "with T and calendar"],
+ ["T12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with T, time zone, and calendar"],
+ ["1970-01-01T12:34:56.987654321[foo=bar]", "with date"],
+ ["1970-01-01T12:34:56.987654321[UTC][foo=bar]", "with date and time zone"],
+ ["1970-01-01T12:34:56.987654321[u-ca=iso8601][foo=bar]", "with date and calendar"],
+ ["1970-01-01T12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with date, time zone, and calendar"],
+ ["1970-01-01T12:34:56.987654321[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainTime.from(arg);
+
+ TemporalHelpers.assertPlainTime(
+ result,
+ 12, 34, 56, 987, 654, 321,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-with-time-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-with-time-designator.js
new file mode 100644
index 0000000000..865af83c0d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-with-time-designator.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: ISO 8601 time designator "T" allowed at the start of PlainTime strings
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const validStrings = [
+ "T00:30",
+ "t00:30",
+ "T0030",
+ "t0030",
+ "T00:30:00",
+ "t00:30:00",
+ "T003000",
+ "t003000",
+ "T00:30:00.000000000",
+ "t00:30:00.000000000",
+ "T003000.000000000",
+ "t003000.000000000",
+];
+validStrings.forEach((arg) => {
+ const result = Temporal.PlainTime.from(arg);
+ TemporalHelpers.assertPlainTime(result, 0, 30, 0, 0, 0, 0, `T prefix is accepted: ${arg}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..7a8e7ad7e9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-with-utc-designator.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: RangeError thrown if a string with UTC designator is used as a PlainTime
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+ "09:00:00Z[UTC]",
+ "09:00:00Z",
+];
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.from(arg),
+ "String with UTC designator should not be valid as a PlainTime"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string.js
new file mode 100644
index 0000000000..b96d370c32
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string.js
@@ -0,0 +1,60 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: Various ISO strings supported
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const tests = [
+ ["15:23", 15, 23, 0, 0, 0, 0],
+ ["15:23:30", 15, 23, 30, 0, 0, 0],
+ ["15:23:30.123", 15, 23, 30, 123, 0, 0],
+ ["15:23:30.123456", 15, 23, 30, 123, 456, 0],
+ ["15:23:30.123456789", 15, 23, 30, 123, 456, 789],
+ ["1976-11-18T15:23:30.1", 15, 23, 30, 100, 0, 0],
+ ["1976-11-18T15:23:30.12", 15, 23, 30, 120, 0, 0],
+ ["1976-11-18T15:23:30.123", 15, 23, 30, 123, 0, 0],
+ ["1976-11-18T15:23:30.1234", 15, 23, 30, 123, 400, 0],
+ ["1976-11-18T15:23:30.12345", 15, 23, 30, 123, 450, 0],
+ ["1976-11-18T15:23:30.123456", 15, 23, 30, 123, 456, 0],
+ ["1976-11-18T15:23:30.1234567", 15, 23, 30, 123, 456, 700],
+ ["1976-11-18T15:23:30.12345678", 15, 23, 30, 123, 456, 780],
+ ["1976-11-18T15:23:30.123456789", 15, 23, 30, 123, 456, 789],
+ ["1976-11-18T15:23:30,12", 15, 23, 30, 120, 0, 0],
+ ["1976-11-18T15:23:30.12\u221202:00", 15, 23, 30, 120, 0, 0],
+ ["152330", 15, 23, 30, 0, 0, 0],
+ ["152330.1", 15, 23, 30, 100, 0, 0],
+ ["152330-08", 15, 23, 30, 0, 0, 0],
+ ["152330.1-08", 15, 23, 30, 100, 0, 0],
+ ["152330-0800", 15, 23, 30, 0, 0, 0],
+ ["152330.1-0800", 15, 23, 30, 100, 0, 0],
+ ["1976-11-18T152330.1+00:00", 15, 23, 30, 100, 0, 0],
+ ["19761118T15:23:30.1+00:00", 15, 23, 30, 100, 0, 0],
+ ["1976-11-18T15:23:30.1+0000", 15, 23, 30, 100, 0, 0],
+ ["1976-11-18T152330.1+0000", 15, 23, 30, 100, 0, 0],
+ ["19761118T15:23:30.1+0000", 15, 23, 30, 100, 0, 0],
+ ["19761118T152330.1+00:00", 15, 23, 30, 100, 0, 0],
+ ["19761118T152330.1+0000", 15, 23, 30, 100, 0, 0],
+ ["+001976-11-18T152330.1+00:00", 15, 23, 30, 100, 0, 0],
+ ["+0019761118T15:23:30.1+00:00", 15, 23, 30, 100, 0, 0],
+ ["+001976-11-18T15:23:30.1+0000", 15, 23, 30, 100, 0, 0],
+ ["+001976-11-18T152330.1+0000", 15, 23, 30, 100, 0, 0],
+ ["+0019761118T15:23:30.1+0000", 15, 23, 30, 100, 0, 0],
+ ["+0019761118T152330.1+00:00", 15, 23, 30, 100, 0, 0],
+ ["+0019761118T152330.1+0000", 15, 23, 30, 100, 0, 0],
+ ["15", 15, 0, 0, 0, 0, 0],
+ ["T15:23:30", 15, 23, 30, 0, 0, 0],
+ ["t152330", 15, 23, 30, 0, 0, 0],
+];
+
+for (const [input, ...expected] of tests) {
+ const result = Temporal.PlainTime.from(input);
+ assert.sameValue(expected.length, 6, input);
+ TemporalHelpers.assertPlainTime(result, ...expected, input);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-wrong-type.js
new file mode 100644
index 0000000000..4a3b01707d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-wrong-type.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainTime
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => Temporal.PlainTime.from(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainTime, "Temporal.PlainTime, object"],
+ [Temporal.PlainTime.prototype, "Temporal.PlainTime.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.PlainTime.from(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 0000000000..12356ae9e9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,46 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: Negative time fields are balanced upwards if the argument is given as ZonedDateTime
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-temporal-totemporaltime step 3.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ ii. 1. Set _plainDateTime_ to ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
+ sec-temporal.plaintime.from step 4:
+ 4. Return ? ToTemporalTime(_temporalTime_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// This code path is encountered if the time zone offset is negative and its
+// absolute value in nanoseconds is greater than the nanosecond field of the
+// exact time's epoch parts
+const tz = TemporalHelpers.specificOffsetTimeZone(-2);
+const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
+
+const time = Temporal.PlainTime.from(datetime);
+
+TemporalHelpers.assertPlainTime(time, 1, 1, 1, 1, 0, 999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..e442577be0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const result = Temporal.PlainTime.from(datetime);
+TemporalHelpers.assertPlainTime(result, 16, 50, 35, 0, 0, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..3cc1f97e08
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => Temporal.PlainTime.from(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..c5cfaeb4b2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaintime.from
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach(notCallable => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainTime.from(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..f9d35b416f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => Temporal.PlainTime.from(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..86c7e90fd6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => Temporal.PlainTime.from(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/builtin.js
new file mode 100644
index 0000000000..05d60e28ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/builtin.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: Tests that Temporal.PlainTime.from meets the requirements for built-in objects
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainTime.from),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.from),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.from),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.from.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..a605d6c2d3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/infinity-throws-rangeerror.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaintime.from
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const base = { hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => Temporal.PlainTime.from({ ...base, [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainTime.from({ ...base, [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/leap-second.js
new file mode 100644
index 0000000000..22694cb864
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/leap-second.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: Leap second is a valid ISO string for PlainTime
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let arg = "2016-12-31T23:59:60";
+
+const result1 = Temporal.PlainTime.from(arg);
+TemporalHelpers.assertPlainTime(
+ result1,
+ 23, 59, 59, 0, 0, 0,
+ "leap second is a valid ISO string for PlainTime"
+);
+
+const result2 = Temporal.PlainTime.from(arg, { overflow: "reject" });
+TemporalHelpers.assertPlainTime(
+ result2,
+ 23, 59, 59, 0, 0, 0,
+ "leap second is a valid ISO string for PlainTime"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+
+const result3 = Temporal.PlainTime.from(arg);
+TemporalHelpers.assertPlainTime(
+ result3,
+ 23, 59, 59, 0, 0, 0,
+ "second: 60 is ignored in property bag for PlainTime"
+);
+
+assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.from(arg, { overflow: "reject" }),
+ "second: 60 is rejected in property bag for PlainTime with overflow: reject"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/length.js
new file mode 100644
index 0000000000..cae3856a6e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: Temporal.PlainTime.from.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.from, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/name.js
new file mode 100644
index 0000000000..ecd305e0a4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: Temporal.PlainTime.from.name is "from"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.from, "name", {
+ value: "from",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/not-a-constructor.js
new file mode 100644
index 0000000000..c93e2cfba9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/not-a-constructor.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: Temporal.PlainTime.from does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainTime.from();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.from), false,
+ "isConstructor(Temporal.PlainTime.from)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/observable-get-overflow-argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/observable-get-overflow-argument-string-invalid.js
new file mode 100644
index 0000000000..54a4045b5f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/observable-get-overflow-argument-string-invalid.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: overflow property is extracted with ISO-invalid string argument.
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get overflow",
+ "get overflow.toString",
+ "call overflow.toString",
+];
+
+let actual = [];
+const object = {
+ get overflow() {
+ actual.push("get overflow");
+ return TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow");
+ }
+};
+
+assert.throws(RangeError, () => Temporal.PlainTime.from("24:60", object));
+assert.compareArray(actual, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-invalid.js
new file mode 100644
index 0000000000..270bd8e3cd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-invalid.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: TypeError thrown when a primitive is passed as the options argument
+features: [Temporal]
+---*/
+
+const values = [null, true, "hello", Symbol("foo"), 1, 1n];
+
+for (const badOptions of values) {
+ assert.throws(TypeError, () => Temporal.PlainTime.from({ hours: 12 }, badOptions));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-object.js
new file mode 100644
index 0000000000..66d716cc23
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-object.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.from
+description: Empty object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.assertPlainTime(
+ Temporal.PlainTime.from({ hour: 12, minute: 34 }, {}), 12, 34, 0, 0, 0, 0,
+ "options may be an empty plain object"
+);
+
+TemporalHelpers.assertPlainTime(
+ Temporal.PlainTime.from({ hour: 12, minute: 34 }, () => {}), 12, 34, 0, 0, 0, 0,
+ "options may be an empty function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-undefined.js
new file mode 100644
index 0000000000..f9c0f77f64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-undefined.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const fields = { hour: 12, minute: 60 };
+
+const explicit = Temporal.PlainTime.from(fields, undefined);
+assert.sameValue(explicit.minute, 59, "default overflow is constrain");
+
+const implicit = Temporal.PlainTime.from(fields);
+assert.sameValue(implicit.minute, 59, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-wrong-type.js
new file mode 100644
index 0000000000..ae4cb12dd2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-wrong-type.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+for (const value of badOptions) {
+ assert.throws(TypeError, () => Temporal.PlainTime.from({ hour: 12, minute: 34 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/order-of-operations.js
new file mode 100644
index 0000000000..e523323801
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/order-of-operations.js
@@ -0,0 +1,55 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: Properties on an object passed to from() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+ // ToTemporalTimeRecord
+ "get fields.hour",
+ "get fields.hour.valueOf",
+ "call fields.hour.valueOf",
+ "get fields.microsecond",
+ "get fields.microsecond.valueOf",
+ "call fields.microsecond.valueOf",
+ "get fields.millisecond",
+ "get fields.millisecond.valueOf",
+ "call fields.millisecond.valueOf",
+ "get fields.minute",
+ "get fields.minute.valueOf",
+ "call fields.minute.valueOf",
+ "get fields.nanosecond",
+ "get fields.nanosecond.valueOf",
+ "call fields.nanosecond.valueOf",
+ "get fields.second",
+ "get fields.second.valueOf",
+ "call fields.second.valueOf",
+];
+const actual = [];
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ hour: 1.7,
+ minute: 1.7,
+ second: 1.7,
+ millisecond: 1.7,
+ microsecond: 1.7,
+ nanosecond: 1.7,
+ calendar: "iso8601",
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ overflow: "constrain",
+}, "options");
+
+const result = Temporal.PlainTime.from(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-constrain.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-constrain.js
new file mode 100644
index 0000000000..5a306b7296
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-constrain.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: constrain value for overflow option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from({ hour: 26 }, { overflow: "constrain" }),
+ 23, 0, 0, 0, 0, 0);
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from({ hour: 22 }, { overflow: "constrain" }),
+ 22, 0, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-invalid-string.js
new file mode 100644
index 0000000000..dc3a9672ec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-invalid-string.js
@@ -0,0 +1,35 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.plaintime.from step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainTime(12),
+ { hour: 12 },
+ "12:00",
+];
+
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const value of validValues) {
+ for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.from(value, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-reject.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-reject.js
new file mode 100644
index 0000000000..9c2d998dc2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-reject.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: reject value for overflow option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => Temporal.PlainTime.from({ hour: 26 }, { overflow: "reject" }));
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from({ hour: 22 }, { overflow: "reject" }),
+ 22, 0, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-undefined.js
new file mode 100644
index 0000000000..8fb5547388
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-undefined.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.plaintime.from step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainTime(12),
+ "12:00",
+];
+validValues.forEach((value) => {
+ const explicit = Temporal.PlainTime.from(value, { overflow: undefined });
+ TemporalHelpers.assertPlainTime(explicit, 12, 0, 0, 0, 0, 0, "overflow is ignored");
+ const implicit = Temporal.PlainTime.from(value, {});
+ TemporalHelpers.assertPlainTime(implicit, 12, 0, 0, 0, 0, 0, "overflow is ignored");
+ const lambda = Temporal.PlainTime.from(value, () => {});
+ TemporalHelpers.assertPlainTime(lambda, 12, 0, 0, 0, 0, 0, "overflow is ignored");
+});
+
+const propertyBag = { hour: 26 };
+const explicit = Temporal.PlainTime.from(propertyBag, { overflow: undefined });
+TemporalHelpers.assertPlainTime(explicit, 23, 0, 0, 0, 0, 0, "default overflow is constrain");
+const implicit = Temporal.PlainTime.from(propertyBag, {});
+TemporalHelpers.assertPlainTime(implicit, 23, 0, 0, 0, 0, 0, "default overflow is constrain");
+const lambda = Temporal.PlainTime.from(propertyBag, () => {});
+TemporalHelpers.assertPlainTime(lambda, 23, 0, 0, 0, 0, 0, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-wrong-type.js
new file mode 100644
index 0000000000..ef10d18d2f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-wrong-type.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.plaintime.from step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainTime(12),
+ { hour: 12 },
+ "12:00",
+];
+validValues.forEach((value) => TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => Temporal.PlainTime.from(value, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainTime(result, 12, 0, 0, 0, 0, 0, descr),
+));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/plaintime-propertybag-no-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/plaintime-propertybag-no-time-units.js
new file mode 100644
index 0000000000..efe7b95c3d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/plaintime-propertybag-no-time-units.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: Missing time units in property bag default to 0
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const props = {};
+assert.throws(TypeError, () => Temporal.PlainTime.from(props), "TypeError if at least one property is not present");
+
+props.minute = 30;
+const result = Temporal.PlainTime.from(props);
+TemporalHelpers.assertPlainTime(result, 0, 30, 0, 0, 0, 0, "missing time units default to 0");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/prop-desc.js
new file mode 100644
index 0000000000..171870e39c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: The "from" property of Temporal.PlainTime
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.from,
+ "function",
+ "`typeof PlainTime.from` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime, "from", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/subclassing-ignored.js
new file mode 100644
index 0000000000..db63caf97f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/subclassing-ignored.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: The receiver is never called when calling from()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.PlainTime,
+ "from",
+ ["12:34:56.987654321"],
+ (result) => TemporalHelpers.assertPlainTime(result, 12, 34, 56, 987, 654, 321),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/year-zero.js
new file mode 100644
index 0000000000..b42a38694c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/year-zero.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.from
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-12-07T03:24:30",
+ "-000000-12-07T03:24:30+01:00",
+ "-000000-12-07T03:24:30+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.from(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/hour-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/hour-undefined.js
new file mode 100644
index 0000000000..db5a1f48e4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/hour-undefined.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime
+description: Hour argument defaults to 0 if not given
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const explicit = new Temporal.PlainTime(undefined);
+TemporalHelpers.assertPlainTime(explicit, 0, 0, 0, 0, 0, 0, "explicit");
+
+const implicit = new Temporal.PlainTime();
+TemporalHelpers.assertPlainTime(implicit, 0, 0, 0, 0, 0, 0, "implicit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..ba30e558fc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/infinity-throws-rangeerror.js
@@ -0,0 +1,60 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainTime throws a RangeError if any value is Infinity
+esid: sec-temporal.plaintime
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => new Temporal.PlainTime(Infinity));
+assert.throws(RangeError, () => new Temporal.PlainTime(0, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainTime(0, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainTime(0, 0, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainTime(0, 0, 0, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainTime(0, 0, 0, 0, 0, Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite hour",
+ [O(Infinity, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ ["get hour.valueOf", "call hour.valueOf"]
+ ],
+ [
+ "infinite minute",
+ [O(1, "hour"), O(Infinity, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ ["get hour.valueOf", "call hour.valueOf", "get minute.valueOf", "call minute.valueOf"]
+ ],
+ [
+ "infinite second",
+ [O(1, "hour"), O(1, "minute"), O(Infinity, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ ["get hour.valueOf", "call hour.valueOf", "get minute.valueOf", "call minute.valueOf", "get second.valueOf", "call second.valueOf"]
+ ],
+ [
+ "infinite millisecond",
+ [O(1, "hour"), O(1, "minute"), O(1, "second"), O(Infinity, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ ["get hour.valueOf", "call hour.valueOf", "get minute.valueOf", "call minute.valueOf", "get second.valueOf", "call second.valueOf", "get millisecond.valueOf", "call millisecond.valueOf"]
+ ],
+ [
+ "infinite microsecond",
+ [O(1, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(Infinity, "microsecond"), O(1, "nanosecond")],
+ ["get hour.valueOf", "call hour.valueOf", "get minute.valueOf", "call minute.valueOf", "get second.valueOf", "call second.valueOf", "get millisecond.valueOf", "call millisecond.valueOf", "get microsecond.valueOf", "call microsecond.valueOf"]
+ ],
+ [
+ "infinite nanosecond",
+ [O(1, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(Infinity, "nanosecond")],
+ ["get hour.valueOf", "call hour.valueOf", "get minute.valueOf", "call minute.valueOf", "get second.valueOf", "call second.valueOf", "get millisecond.valueOf", "call millisecond.valueOf", "get microsecond.valueOf", "call microsecond.valueOf", "get nanosecond.valueOf", "call nanosecond.valueOf"]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.PlainTime(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/length.js
new file mode 100644
index 0000000000..50e2cd496b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime
+description: Temporal.PlainTime.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/microsecond-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/microsecond-undefined.js
new file mode 100644
index 0000000000..076adb01ee
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/microsecond-undefined.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime
+description: Microsecond argument defaults to 0 if not given
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const args = [12, 34, 56, 123];
+
+const explicit = new Temporal.PlainTime(...args, undefined);
+TemporalHelpers.assertPlainTime(explicit, ...args, 0, 0, "explicit");
+
+const implicit = new Temporal.PlainTime(...args);
+TemporalHelpers.assertPlainTime(implicit, ...args, 0, 0, "implicit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/millisecond-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/millisecond-undefined.js
new file mode 100644
index 0000000000..cd32d9895b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/millisecond-undefined.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime
+description: Millisecond argument defaults to 0 if not given
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const args = [12, 34, 56];
+
+const explicit = new Temporal.PlainTime(...args, undefined);
+TemporalHelpers.assertPlainTime(explicit, ...args, 0, 0, 0, "explicit");
+
+const implicit = new Temporal.PlainTime(...args);
+TemporalHelpers.assertPlainTime(implicit, ...args, 0, 0, 0, "implicit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/minute-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/minute-undefined.js
new file mode 100644
index 0000000000..877d433cea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/minute-undefined.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime
+description: Minute argument defaults to 0 if not given
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const hour = 12;
+
+const explicit = new Temporal.PlainTime(hour, undefined);
+TemporalHelpers.assertPlainTime(explicit, hour, 0, 0, 0, 0, 0, "explicit");
+
+const implicit = new Temporal.PlainTime(hour);
+TemporalHelpers.assertPlainTime(implicit, hour, 0, 0, 0, 0, 0, "implicit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/name.js
new file mode 100644
index 0000000000..766cffff6c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime
+description: Temporal.PlainTime.name is "PlainTime"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime, "name", {
+ value: "PlainTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/nanosecond-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/nanosecond-undefined.js
new file mode 100644
index 0000000000..726f0efe4c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/nanosecond-undefined.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime
+description: Nanosecond argument defaults to 0 if not given
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const args = [12, 34, 56, 123, 456];
+
+const explicit = new Temporal.PlainTime(...args, undefined);
+TemporalHelpers.assertPlainTime(explicit, ...args, 0, "explicit");
+
+const implicit = new Temporal.PlainTime(...args);
+TemporalHelpers.assertPlainTime(implicit, ...args, 0, "implicit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..a3ca04739f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,60 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainDate throws a RangeError if any value is -Infinity
+esid: sec-temporal.plaintime
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => new Temporal.PlainTime(-Infinity));
+assert.throws(RangeError, () => new Temporal.PlainTime(0, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainTime(0, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainTime(0, 0, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainTime(0, 0, 0, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainTime(0, 0, 0, 0, 0, -Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite hour",
+ [O(-Infinity, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ ["get hour.valueOf", "call hour.valueOf"]
+ ],
+ [
+ "infinite minute",
+ [O(1, "hour"), O(-Infinity, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ ["get hour.valueOf", "call hour.valueOf", "get minute.valueOf", "call minute.valueOf"]
+ ],
+ [
+ "infinite second",
+ [O(1, "hour"), O(1, "minute"), O(-Infinity, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ ["get hour.valueOf", "call hour.valueOf", "get minute.valueOf", "call minute.valueOf", "get second.valueOf", "call second.valueOf"]
+ ],
+ [
+ "infinite millisecond",
+ [O(1, "hour"), O(1, "minute"), O(1, "second"), O(-Infinity, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ ["get hour.valueOf", "call hour.valueOf", "get minute.valueOf", "call minute.valueOf", "get second.valueOf", "call second.valueOf", "get millisecond.valueOf", "call millisecond.valueOf"]
+ ],
+ [
+ "infinite microsecond",
+ [O(1, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(-Infinity, "microsecond"), O(1, "nanosecond")],
+ ["get hour.valueOf", "call hour.valueOf", "get minute.valueOf", "call minute.valueOf", "get second.valueOf", "call second.valueOf", "get millisecond.valueOf", "call millisecond.valueOf", "get microsecond.valueOf", "call microsecond.valueOf"]
+ ],
+ [
+ "infinite nanosecond",
+ [O(1, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(-Infinity, "nanosecond")],
+ ["get hour.valueOf", "call hour.valueOf", "get minute.valueOf", "call minute.valueOf", "get second.valueOf", "call second.valueOf", "get millisecond.valueOf", "call millisecond.valueOf", "get microsecond.valueOf", "call microsecond.valueOf", "get nanosecond.valueOf", "call nanosecond.valueOf"]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.PlainTime(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/negative-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/negative-zero.js
new file mode 100644
index 0000000000..acb69c0b37
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/negative-zero.js
@@ -0,0 +1,15 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime
+description: Negative zero arguments are treated as zero.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(-0, -0, -0, -0, -0, -0);
+TemporalHelpers.assertPlainTime(plainTime, 0, 0, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prop-desc.js
new file mode 100644
index 0000000000..811fd97303
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime
+description: The "PlainTime" property of Temporal
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime,
+ "function",
+ "`typeof PlainTime` is `function`"
+);
+
+verifyProperty(Temporal, "PlainTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration-max.js
new file mode 100644
index 0000000000..bbaded9b35
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration-max.js
@@ -0,0 +1,58 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: Maximum allowed duration
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+
+const maxCases = [
+ ["P4294967295Y104249991374DT7H36M31.999999999S", "string with max years"],
+ [{ years: 4294967295, days: 104249991374, nanoseconds: 27391999999999 }, "property bag with max years"],
+ ["P4294967295M104249991374DT7H36M31.999999999S", "string with max weeks"],
+ [{ months: 4294967295, days: 104249991374, nanoseconds: 27391999999999 }, "property bag with max months"],
+ ["P4294967295W104249991374DT7H36M31.999999999S", "string with max weeks"],
+ [{ weeks: 4294967295, days: 104249991374, nanoseconds: 27391999999999 }, "property bag with max weeks"],
+ ["P104249991374DT7H36M31.999999999S", "string with max days"],
+ [{ days: 104249991374, nanoseconds: 27391999999999 }, "property bag with max days"],
+ ["PT2501999792983H36M31.999999999S", "string with max hours"],
+ [{ hours: 2501999792983, nanoseconds: 2191999999999 }, "property bag with max hours"],
+ ["PT150119987579016M31.999999999S", "string with max minutes"],
+ [{ minutes: 150119987579016, nanoseconds: 31999999999 }, "property bag with max minutes"],
+ ["PT9007199254740991.999999999S", "string with max seconds"],
+ [{ seconds: 9007199254740991, nanoseconds: 999999999 }, "property bag with max seconds"],
+];
+
+for (const [arg, descr] of maxCases) {
+ const result = instance.add(arg);
+ TemporalHelpers.assertPlainTime(result, 7, 36, 31, 999, 999, 999, `operation succeeds with ${descr}`);
+}
+
+const minCases = [
+ ["-P4294967295Y104249991374DT7H36M31.999999999S", "string with min years"],
+ [{ years: -4294967295, days: -104249991374, nanoseconds: -27391999999999 }, "property bag with min years"],
+ ["-P4294967295M104249991374DT7H36M31.999999999S", "string with min months"],
+ [{ months: -4294967295, days: -104249991374, nanoseconds: -27391999999999 }, "property bag with min months"],
+ ["-P4294967295W104249991374DT7H36M31.999999999S", "string with min weeks"],
+ [{ weeks: -4294967295, days: -104249991374, nanoseconds: -27391999999999 }, "property bag with min weeks"],
+ ["-P104249991374DT7H36M31.999999999S", "string with min days"],
+ [{ days: -104249991374, nanoseconds: -27391999999999 }, "property bag with min days"],
+ ["-PT2501999792983H36M31.999999999S", "string with min hours"],
+ [{ hours: -2501999792983, nanoseconds: -2191999999999 }, "property bag with min hours"],
+ ["-PT150119987579016M31.999999999S", "string with min minutes"],
+ [{ minutes: -150119987579016, nanoseconds: -31999999999 }, "property bag with min minutes"],
+ ["-PT9007199254740991.999999999S", "string with min seconds"],
+ [{ seconds: -9007199254740991, nanoseconds: -999999999 }, "property bag with min seconds"],
+];
+
+for (const [arg, descr] of minCases) {
+ const result = instance.add(arg);
+ TemporalHelpers.assertPlainTime(result, 16, 23, 28, 0, 0, 1, `operation succeeds with ${descr}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration-out-of-range.js
new file mode 100644
index 0000000000..7639ffc6f4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration-out-of-range.js
@@ -0,0 +1,75 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: Duration-like argument that is out of range
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+
+const cases = [
+ // 2^32 = 4294967296
+ ["P4294967296Y", "string with years > max"],
+ [{ years: 4294967296 }, "property bag with years > max"],
+ ["-P4294967296Y", "string with years < min"],
+ [{ years: -4294967296 }, "property bag with years < min"],
+ ["P4294967296M", "string with months > max"],
+ [{ months: 4294967296 }, "property bag with months > max"],
+ ["-P4294967296M", "string with months < min"],
+ [{ months: -4294967296 }, "property bag with months < min"],
+ ["P4294967296W", "string with weeks > max"],
+ [{ weeks: 4294967296 }, "property bag with weeks > max"],
+ ["-P4294967296W", "string with weeks < min"],
+ [{ weeks: -4294967296 }, "property bag with weeks < min"],
+
+ // ceil(max safe integer / 86400) = 104249991375
+ ["P104249991375D", "string with days > max"],
+ [{ days: 104249991375 }, "property bag with days > max"],
+ ["P104249991374DT24H", "string where hours balance into days > max"],
+ [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"],
+ ["-P104249991375D", "string with days < min"],
+ [{ days: -104249991375 }, "property bag with days < min"],
+ ["-P104249991374DT24H", "string where hours balance into days < min"],
+ [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"],
+
+ // ceil(max safe integer / 3600) = 2501999792984
+ ["PT2501999792984H", "string with hours > max"],
+ [{ hours: 2501999792984 }, "property bag with hours > max"],
+ ["PT2501999792983H60M", "string where minutes balance into hours > max"],
+ [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"],
+ ["-PT2501999792984H", "string with hours < min"],
+ [{ hours: -2501999792984 }, "property bag with hours < min"],
+ ["-PT2501999792983H60M", "string where minutes balance into hours < min"],
+ [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"],
+
+ // ceil(max safe integer / 60) = 150119987579017
+ ["PT150119987579017M", "string with minutes > max"],
+ [{ minutes: 150119987579017 }, "property bag with minutes > max"],
+ ["PT150119987579016M60S", "string where seconds balance into minutes > max"],
+ [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"],
+ ["-PT150119987579017M", "string with minutes < min"],
+ [{ minutes: -150119987579017 }, "property bag with minutes < min"],
+ ["-PT150119987579016M60S", "string where seconds balance into minutes < min"],
+ [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"],
+
+ // 2^53 = 9007199254740992
+ ["PT9007199254740992S", "string with seconds > max"],
+ [{ seconds: 9007199254740992 }, "property bag with seconds > max"],
+ [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"],
+ ["-PT9007199254740992S", "string with seconds < min"],
+ [{ seconds: -9007199254740992 }, "property bag with seconds < min"],
+ [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"],
+];
+
+for (const [arg, descr] of cases) {
+ assert.throws(RangeError, () => instance.add(arg), `${descr} is out of range`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration-precision-exact-numerical-values.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration-precision-exact-numerical-values.js
new file mode 100644
index 0000000000..571afaef87
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration-precision-exact-numerical-values.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: >
+ Duration-like argument performs the range check with minimal floating point
+ precision loss
+features: [Temporal]
+---*/
+
+// Based on a test case by André Bargull
+
+const instance = new Temporal.PlainTime();
+
+const cases = [
+ [
+ {
+ milliseconds: 4503599627370497_000, // ℝ(𝔽(4503599627370497000)) = 4503599627370497024
+ microseconds: 4503599627370495_000000, // ℝ(𝔽(4503599627370495000000)) = 4503599627370494951424
+ },
+ // 4503599627370497024 / 1000 + 4503599627370494951424 / 1000000 is
+ // 9007199254740991.975424, which is below the limit of 2**53
+ "case where floating point inaccuracy brings total below limit, positive"
+ ],
+ [
+ {
+ milliseconds: -4503599627370497_000,
+ microseconds: -4503599627370495_000000,
+ },
+ "case where floating point inaccuracy brings total below limit, negative"
+ ],
+];
+
+for (const [arg, descr] of cases) {
+ instance.add(arg); // should not throw
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration.js
new file mode 100644
index 0000000000..961d154bbb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: Duration arguments are supported.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(15, 23, 30, 123, 456, 789);
+const duration = Temporal.Duration.from("PT16H");
+TemporalHelpers.assertPlainTime(plainTime.add(duration),
+ 7, 23, 30, 123, 456, 789);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-higher-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-higher-units.js
new file mode 100644
index 0000000000..54462bcdca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-higher-units.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: Higher units are ignored.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(15, 23, 30, 123, 456, 789);
+const values = [
+ new Temporal.Duration(0, 0, 0, 1),
+ new Temporal.Duration(0, 0, 1),
+ new Temporal.Duration(0, 1),
+ new Temporal.Duration(1),
+ { days: 1 },
+ { weeks: 1 },
+ { months: 1 },
+ { years: 1 },
+ "P1D",
+ "P1W",
+ "P1M",
+ "P1Y",
+];
+for (const value of values) {
+ TemporalHelpers.assertPlainTime(plainTime.add(value),
+ 15, 23, 30, 123, 456, 789);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-invalid-property.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-invalid-property.js
new file mode 100644
index 0000000000..c3c89846a1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-invalid-property.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: temporalDurationLike object must contain at least one correctly spelled property
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(15, 30, 45, 987, 654, 321);
+
+assert.throws(
+ TypeError,
+ () => instance.add({}),
+ "Throws TypeError if no property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.add({ nonsense: true }),
+ "Throws TypeError if no recognized property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.add({ sign: 1 }),
+ "Sign property is not recognized"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-mixed-sign.js
new file mode 100644
index 0000000000..b95c4a0269
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-mixed-sign.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: Positive and negative values in the temporalDurationLike argument are not acceptable
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(15, 30, 45, 987, 654, 321);
+
+assert.throws(
+ RangeError,
+ () => instance.add({ hours: 1, minutes: -30 }),
+ `mixed positive and negative values always throw`
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-not-object.js
new file mode 100644
index 0000000000..76496e794e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-not-object.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: Passing a primitive other than string to add() throws
+features: [Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(15, 30, 45, 987, 654, 321);
+assert.throws(TypeError, () => instance.add(undefined), "undefined");
+assert.throws(TypeError, () => instance.add(null), "null");
+assert.throws(TypeError, () => instance.add(true), "boolean");
+assert.throws(RangeError, () => instance.add(""), "empty string");
+assert.throws(TypeError, () => instance.add(Symbol()), "Symbol");
+assert.throws(TypeError, () => instance.add(7), "number");
+assert.throws(TypeError, () => instance.add(7n), "bigint");
+assert.throws(TypeError, () => instance.add([]), "array");
+assert.throws(TypeError, () => instance.add(() => {}), "function");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-object.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-object.js
new file mode 100644
index 0000000000..665cf6f3b6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-object.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: Plain object arguments are supported.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(15, 23, 30, 123, 456, 789);
+TemporalHelpers.assertPlainTime(plainTime.add({ hours: 16 }),
+ 7, 23, 30, 123, 456, 789, "add 16 hours across midnight boundary");
+TemporalHelpers.assertPlainTime(plainTime.add({ minutes: 45 }),
+ 16, 8, 30, 123, 456, 789, "add 45 minutes");
+TemporalHelpers.assertPlainTime(plainTime.add({ seconds: 800 }),
+ 15, 36, 50, 123, 456, 789, "add 800 seconds");
+TemporalHelpers.assertPlainTime(plainTime.add({ milliseconds: 800 }),
+ 15, 23, 30, 923, 456, 789, "add 800 milliseconds");
+TemporalHelpers.assertPlainTime(plainTime.add({ microseconds: 800 }),
+ 15, 23, 30, 124, 256, 789, "add 800 microseconds");
+TemporalHelpers.assertPlainTime(plainTime.add({ nanoseconds: 300 }),
+ 15, 23, 30, 123, 457, 89, "add 300 nanoseconds");
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("07:23:30.123456789").add({ hours: -16 }),
+ 15, 23, 30, 123, 456, 789, "add -16 hours across midnight boundary");
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("16:08:30.123456789").add({ minutes: -45 }),
+ 15, 23, 30, 123, 456, 789, "add -45 minutes");
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("15:36:50.123456789").add({ seconds: -800 }),
+ 15, 23, 30, 123, 456, 789, "add -800 seconds");
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("15:23:30.923456789").add({ milliseconds: -800 }),
+ 15, 23, 30, 123, 456, 789, "add -800 milliseconds");
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("15:23:30.124256789").add({ microseconds: -800 }),
+ 15, 23, 30, 123, 456, 789, "add -800 microseconds");
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("15:23:30.123457089").add({ nanoseconds: -300 }),
+ 15, 23, 30, 123, 456, 789, "add -300 nanoseconds");
+TemporalHelpers.assertPlainTime(plainTime.add({ minute: 1, hours: 1 }),
+ 16, 23, 30, 123, 456, 789, "misspelled property is ignored");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-singular-properties.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-singular-properties.js
new file mode 100644
index 0000000000..415dbad921
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-singular-properties.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: Singular properties in the property bag are always ignored
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(15, 30, 45, 987, 654, 321);
+
+[
+ { year: 1 },
+ { month: 2 },
+ { week: 3 },
+ { day: 4 },
+ { hour: 5 },
+ { minute: 6 },
+ { second: 7 },
+ { millisecond: 8 },
+ { microsecond: 9 },
+ { nanosecond: 10 },
+].forEach((badObject) => {
+ assert.throws(TypeError, () => instance.add(badObject),
+ "Throw TypeError if temporalDurationLike is not valid");
+});
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string-duration-too-large.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string-duration-too-large.js
new file mode 100644
index 0000000000..e155515611
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string-duration-too-large.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: >
+ ParseTemporalDurationString throws a RangeError when the result is too large.
+features: [Temporal]
+---*/
+
+// Number string too long to be representable as a Number value.
+var ones = "1".repeat(1000);
+assert.sameValue(Number(ones), Infinity);
+
+var time = new Temporal.PlainTime();
+var str = "PT" + ones + "S";
+
+assert.throws(RangeError, () => time.add(str));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string-fractional-units-rounding-mode.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string-fractional-units-rounding-mode.js
new file mode 100644
index 0000000000..db31649d15
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string-fractional-units-rounding-mode.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: Strings with fractional duration units are rounded with the correct rounding mode
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const midnight = new Temporal.PlainTime();
+
+TemporalHelpers.assertPlainTime(midnight.add("PT1.03125H"), 1, 1, 52, 500, 0, 0,
+ "positive fractional units rounded with correct rounding mode");
+TemporalHelpers.assertPlainTime(midnight.add("-PT1.03125H"), 22, 58, 7, 500, 0, 0,
+ "negative fractional units rounded with correct rounding mode");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string-negative-fractional-units.js
new file mode 100644
index 0000000000..e604c8b210
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string-negative-fractional-units.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+
+const resultHours = instance.add("-PT24.567890123H");
+TemporalHelpers.assertPlainTime(resultHours, 23, 25, 55, 595, 557, 200, "negative fractional hours");
+
+const resultMinutes = instance.add("-PT1440.567890123M");
+TemporalHelpers.assertPlainTime(resultMinutes, 23, 59, 25, 926, 592, 620, "negative fractional minutes");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string.js
new file mode 100644
index 0000000000..370e3af18c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: A string is parsed into the correct object when passed as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = Temporal.PlainTime.from({ hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+const result = instance.add("PT3M");
+TemporalHelpers.assertPlainTime(result, 12, 37, 56, 987, 654, 321);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/balance-negative-time-units.js
new file mode 100644
index 0000000000..ee6367ad1e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/balance-negative-time-units.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-addtime step 8:
+ 8. Return ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal.plaintime.prototype.add step 4:
+ 4. Let _result_ be ? AddTime(_temporalTime_.[[ISOHour]], _temporalTime_.[[ISOMinute]], _temporalTime_.[[ISOSecond]], _temporalTime_.[[ISOMillisecond]], _temporalTime_.[[ISOMicrosecond]], _temporalTime_.[[ISONanosecond]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]]).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(1, 1, 1, 1, 1, 1);
+
+const result1 = time.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainTime(result1, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
+
+const result2 = time.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainTime(result2, 1, 1, 1, 0, 999, 1, "microseconds balance");
+
+const result3 = time.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainTime(result3, 1, 1, 0, 999, 1, 1, "milliseconds balance");
+
+const result4 = time.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainTime(result4, 1, 0, 59, 1, 1, 1, "seconds balance");
+
+const result5 = time.add(new Temporal.Duration(0, 0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainTime(result5, 0, 59, 1, 1, 1, 1, "minutes balance");
+
+const result6 = time.add(new Temporal.Duration(0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainTime(result6, 23, 1, 1, 1, 1, 1, "hours mod 24");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/branding.js
new file mode 100644
index 0000000000..6d04681c4e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const add = Temporal.PlainTime.prototype.add;
+
+assert.sameValue(typeof add, "function");
+
+const args = [new Temporal.Duration(0, 0, 0, 0, 5)];
+
+assert.throws(TypeError, () => add.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => add.apply(null, args), "null");
+assert.throws(TypeError, () => add.apply(true, args), "true");
+assert.throws(TypeError, () => add.apply("", args), "empty string");
+assert.throws(TypeError, () => add.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => add.apply(1, args), "1");
+assert.throws(TypeError, () => add.apply({}, args), "plain object");
+assert.throws(TypeError, () => add.apply(Temporal.PlainTime, args), "Temporal.PlainTime");
+assert.throws(TypeError, () => add.apply(Temporal.PlainTime.prototype, args), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/builtin.js
new file mode 100644
index 0000000000..3d3dc4cccd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: >
+ Tests that Temporal.PlainTime.prototype.add
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainTime.prototype.add),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.prototype.add),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.prototype.add),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.prototype.add.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..b6c769b42b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainTime.prototype.add throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.plaintime.prototype.add
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainTime.from({ hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/length.js
new file mode 100644
index 0000000000..825d286d11
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: Temporal.PlainTime.prototype.add.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.add, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/name.js
new file mode 100644
index 0000000000..053decb679
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: Temporal.PlainTime.prototype.add.name is "add".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.add, "name", {
+ value: "add",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..2ec5d1f3f6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainTime.prototype.add throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.plaintime.prototype.add
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainTime.from({ hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: -Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/non-integer-throws-rangeerror.js
new file mode 100644
index 0000000000..f9d007e9bc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/non-integer-throws-rangeerror.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(15, 30, 45, 987, 654, 321);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.add({ [field]: -1.5 }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/not-a-constructor.js
new file mode 100644
index 0000000000..e375de5ad7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: >
+ Temporal.PlainTime.prototype.add does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainTime.prototype.add();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.prototype.add), false,
+ "isConstructor(Temporal.PlainTime.prototype.add)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/options-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/options-ignored.js
new file mode 100644
index 0000000000..e8f2bcc5cd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/options-ignored.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: Options argument is ignored.
+includes: [temporalHelpers.js]
+features: [Symbol, Temporal]
+---*/
+
+const values = [
+ undefined,
+ null,
+ true,
+ "hello",
+ Symbol("foo"),
+ 1,
+ 1n,
+ {},
+ () => {},
+ { get overflow() { throw new Test262Error("should not get overflow") } },
+];
+
+const time = Temporal.PlainTime.from("15:23:30.123456789");
+for (const options of values) {
+ TemporalHelpers.assertPlainTime(time.add({ hours: 1 }, options),
+ 16, 23, 30, 123, 456, 789);
+}
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/order-of-operations.js
new file mode 100644
index 0000000000..ac4524dc17
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/order-of-operations.js
@@ -0,0 +1,62 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: Properties on an object passed to add() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const expected = [
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.months.valueOf",
+ "call fields.months.valueOf",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.weeks.valueOf",
+ "call fields.weeks.valueOf",
+ "get fields.years",
+ "get fields.years.valueOf",
+ "call fields.years.valueOf",
+];
+const actual = [];
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+const result = instance.add(fields);
+TemporalHelpers.assertPlainTime(result, 13, 35, 57, 988, 655, 322);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/precision-exact-mathematical-values-1.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/precision-exact-mathematical-values-1.js
new file mode 100644
index 0000000000..5c765a761b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/precision-exact-mathematical-values-1.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: >
+ Duration components are precise mathematical integers.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let duration = Temporal.Duration.from({
+ microseconds: Number.MAX_SAFE_INTEGER,
+ nanoseconds: 1000,
+});
+
+let time = Temporal.PlainTime.from({
+ microsecond: 1,
+});
+
+let result = time.add(duration);
+
+TemporalHelpers.assertPlainTime(result, 23, 47, 34, 740, 993, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/precision-exact-mathematical-values-2.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/precision-exact-mathematical-values-2.js
new file mode 100644
index 0000000000..3bee5992cc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/precision-exact-mathematical-values-2.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: >
+ Duration components are precise mathematical integers.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let duration = Temporal.Duration.from({
+ seconds: Number.MAX_SAFE_INTEGER,
+ nanoseconds: 999_999_999,
+});
+
+let time = new Temporal.PlainTime(0, 0, 0, 0, 0, 0);
+
+let result = time.add(duration);
+
+TemporalHelpers.assertPlainTime(result, 7, 36, 31, 999, 999, 999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/prop-desc.js
new file mode 100644
index 0000000000..293e29f763
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: The "add" property of Temporal.PlainTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.prototype.add,
+ "function",
+ "`typeof PlainTime.prototype.add` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime.prototype, "add", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/subclassing-ignored.js
new file mode 100644
index 0000000000..2f88171099
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/subclassing-ignored.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: Objects of a subclass are never created as return values for add()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainTime,
+ [12, 34, 56, 987, 654, 321],
+ "add",
+ [{ nanoseconds: 1 }],
+ (result) => TemporalHelpers.assertPlainTime(result, 12, 34, 56, 987, 654, 322),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/constructor.js
new file mode 100644
index 0000000000..43a54d65c6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/constructor.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.constructor
+description: Test for Temporal.PlainTime.prototype.constructor.
+info: The initial value of Temporal.PlainTime.prototype.constructor is %Temporal.PlainTime%.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype, "constructor", {
+ value: Temporal.PlainTime,
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-cast.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-cast.js
new file mode 100644
index 0000000000..114c98ea27
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-cast.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: equals() casts its argument
+features: [Temporal]
+---*/
+
+const t1 = Temporal.PlainTime.from("08:44:15.321");
+
+assert.sameValue(t1.equals({ hour: 14, minute: 23, second: 30, millisecond: 123 }), false, "object");
+assert.sameValue(t1.equals({ hour: 8, minute: 44, second: 15, millisecond: 321 }), true, "object");
+assert.sameValue(t1.equals("14:23:30.123"), false, "string");
+assert.sameValue(t1.equals("08:44:15.321"), true, "string");
+assert.throws(TypeError, () => t1.equals({}), "no properties");
+assert.throws(TypeError, () => t1.equals({ hours: 8 }), "only plural property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-number.js
new file mode 100644
index 0000000000..d2909de6f6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-number.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: A number is invalid in place of an ISO string for Temporal.PlainTime
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const numbers = [
+ 1,
+ -123456.987654321,
+ 1234567,
+ 123456.9876543219,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.equals(arg),
+ `A number (${arg}) is not a valid ISO string for PlainTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..baf1fa2472
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-calendar-annotation.js
@@ -0,0 +1,42 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["12:34:56.987654321[u-ca=iso8601]", "without time zone"],
+ ["12:34:56.987654321[UTC][u-ca=iso8601]", "with time zone"],
+ ["12:34:56.987654321[!u-ca=iso8601]", "with ! and no time zone"],
+ ["12:34:56.987654321[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["T12:34:56.987654321[u-ca=iso8601]", "with T and no time zone"],
+ ["T12:34:56.987654321[UTC][u-ca=iso8601]", "with T and time zone"],
+ ["T12:34:56.987654321[!u-ca=iso8601]", "with T, !, and no time zone"],
+ ["T12:34:56.987654321[UTC][!u-ca=iso8601]", "with T, !, and time zone"],
+ ["1970-01-01T12:34:56.987654321[u-ca=iso8601]", "with date and no time zone"],
+ ["1970-01-01T12:34:56.987654321[UTC][u-ca=iso8601]", "with date and time zone"],
+ ["1970-01-01T12:34:56.987654321[!u-ca=iso8601]", "with !, date, and no time zone"],
+ ["1970-01-01T12:34:56.987654321[UTC][!u-ca=iso8601]", "with !, date, and time zone"],
+ ["12:34:56.987654321[u-ca=hebrew]", "calendar annotation ignored"],
+ ["12:34:56.987654321[u-ca=unknown]", "calendar annotation ignored even if unknown calendar"],
+ ["12:34:56.987654321[!u-ca=unknown]", "calendar annotation ignored even if unknown calendar with !"],
+ ["1970-01-01T12:34:56.987654321[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..45706fa2c0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "00:00[!foo=bar]",
+ "T00:00[!foo=bar]",
+ "1970-01-01T00:00[!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..eacfcf8bda
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-date-with-utc-offset.js
@@ -0,0 +1,52 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const validStrings = [
+ "12:34:56.987654321+00:00",
+ "12:34:56.987654321+00:00[UTC]",
+ "12:34:56.987654321+00:00[!UTC]",
+ "12:34:56.987654321-02:30[America/St_Johns]",
+ "1976-11-18T12:34:56.987654321+00:00",
+ "1976-11-18T12:34:56.987654321+00:00[UTC]",
+ "1976-11-18T12:34:56.987654321+00:00[!UTC]",
+ "1976-11-18T12:34:56.987654321-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `"${arg}" is a valid UTC offset with time for PlainTime`
+ );
+}
+
+const invalidStrings = [
+ "2022-09-15Z",
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `"${arg}" UTC offset without time is not valid for PlainTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..e4989f5525
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-multiple-calendar.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..d3026d1fb9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-multiple-time-zone.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "00:00[UTC][UTC]",
+ "T00:00[UTC][UTC]",
+ "1970-01-01T00:00[UTC][UTC]",
+ "1970-01-01T00:00[!UTC][UTC]",
+ "1970-01-01T00:00[UTC][!UTC]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00[UTC][foo=bar][UTC]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-no-implicit-midnight.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-no-implicit-midnight.js
new file mode 100644
index 0000000000..b918920ca4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-no-implicit-midnight.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: RangeError thrown if a date-only string is passed in a PlainTime context
+features: [Temporal, arrow-function]
+---*/
+
+const arg = "2019-10-01";
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ "Date-only string throws, does not implicitly convert to midnight"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-time-designator-required-for-disambiguation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-time-designator-required-for-disambiguation.js
new file mode 100644
index 0000000000..29548d9e14
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-time-designator-required-for-disambiguation.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: ISO 8601 time designator "T" required in cases of ambiguity
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+TemporalHelpers.ISO.plainTimeStringsAmbiguous().forEach((string) => {
+ let arg = string;
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `'${arg}' is ambiguous and requires T prefix`
+ );
+ // The same string with a T prefix should not throw:
+ arg = `T${string}`;
+ instance.equals(arg);
+
+ arg = ` ${string}`;
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `space is not accepted as a substitute for T prefix: '${arg}'`
+ );
+});
+
+// None of these should throw without a T prefix, because they are unambiguously time strings:
+TemporalHelpers.ISO.plainTimeStringsUnambiguous().forEach(
+ (arg) => instance.equals(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-time-separators.js
new file mode 100644
index 0000000000..b7da0089c8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-time-separators.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1976-11-18T12:34:56.987654321", "uppercase T"],
+ ["1976-11-18t12:34:56.987654321", "lowercase T"],
+ ["1976-11-18 12:34:56.987654321", "space between date and time"],
+ ["T12:34:56.987654321", "time-only uppercase T"],
+ ["t12:34:56.987654321", "time-only lowercase T"],
+];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..25e895d7f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-time-zone-annotation.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["12:34:56.987654321[Asia/Kolkata]", "named, with no offset"],
+ ["12:34:56.987654321[!Europe/Vienna]", "named, with ! and no offset"],
+ ["12:34:56.987654321[+00:00]", "numeric, with no offset"],
+ ["12:34:56.987654321[!-02:30]", "numeric, with ! and no offset"],
+ ["T12:34:56.987654321[UTC]", "named, with T and no offset"],
+ ["T12:34:56.987654321[!Africa/Abidjan]", "named, with T, !, and no offset"],
+ ["T12:34:56.987654321[+01:00]", "numeric, with T and no offset"],
+ ["T12:34:56.987654321[!-08:00]", "numeric, with T, !, and no offset"],
+ ["12:34:56.987654321+00:00[America/Sao_Paulo]", "named, with offset"],
+ ["12:34:56.987654321+00:00[!Asia/Tokyo]", "named, with ! and offset"],
+ ["12:34:56.987654321+00:00[-02:30]", "numeric, with offset"],
+ ["12:34:56.987654321+00:00[!+00:00]", "numeric, with ! and offset"],
+ ["T12:34:56.987654321+00:00[America/New_York]", "named, with T and offset"],
+ ["T12:34:56.987654321+00:00[!UTC]", "named, with T, !, and offset"],
+ ["T12:34:56.987654321+00:00[-08:00]", "numeric, with T and offset"],
+ ["T12:34:56.987654321+00:00[!+01:00]", "numeric, with T, !, and offset"],
+ ["1970-01-01T12:34:56.987654321[Africa/Lagos]", "named, with date and no offset"],
+ ["1970-01-01T12:34:56.987654321[!America/Vancouver]", "named, with date, !, and no offset"],
+ ["1970-01-01T12:34:56.987654321[+00:00]", "numeric, with date and no offset"],
+ ["1970-01-01T12:34:56.987654321[!-02:30]", "numeric, with date, !, and no offset"],
+ ["1970-01-01T12:34:56.987654321+00:00[Europe/London]", "named, with date and offset"],
+ ["1970-01-01T12:34:56.987654321+00:00[!Asia/Seoul]", "named, with date, offset, and !"],
+ ["1970-01-01T12:34:56.987654321+00:00[+01:00]", "numeric, with date and offset"],
+ ["1970-01-01T12:34:56.987654321+00:00[!-08:00]", "numeric, with date, offset, and !"],
+];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..62c6c39c71
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-unknown-annotation.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["12:34:56.987654321[foo=bar]", "alone"],
+ ["12:34:56.987654321[UTC][foo=bar]", "with time zone"],
+ ["12:34:56.987654321[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["T12:34:56.987654321[foo=bar]", "with T"],
+ ["T12:34:56.987654321[UTC][foo=bar]", "with T and time zone"],
+ ["T12:34:56.987654321[u-ca=iso8601][foo=bar]", "with T and calendar"],
+ ["T12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with T, time zone, and calendar"],
+ ["1970-01-01T12:34:56.987654321[foo=bar]", "with date"],
+ ["1970-01-01T12:34:56.987654321[UTC][foo=bar]", "with date and time zone"],
+ ["1970-01-01T12:34:56.987654321[u-ca=iso8601][foo=bar]", "with date and calendar"],
+ ["1970-01-01T12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with date, time zone, and calendar"],
+ ["1970-01-01T12:34:56.987654321[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-with-time-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-with-time-designator.js
new file mode 100644
index 0000000000..8177135fd5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-with-time-designator.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: ISO 8601 time designator "T" allowed at the start of PlainTime strings
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.PlainTime(0, 30, 0, 0, 0, 0);
+const validStrings = [
+ "T00:30",
+ "t00:30",
+ "T0030",
+ "t0030",
+ "T00:30:00",
+ "t00:30:00",
+ "T003000",
+ "t003000",
+ "T00:30:00.000000000",
+ "t00:30:00.000000000",
+ "T003000.000000000",
+ "t003000.000000000",
+];
+validStrings.forEach((arg) => {
+ const result = instance.equals(arg);
+ assert.sameValue(result, true, `T prefix is accepted: ${arg}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..1dd6f0ebb1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-with-utc-designator.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: RangeError thrown if a string with UTC designator is used as a PlainTime
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+ "09:00:00Z[UTC]",
+ "09:00:00Z",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ "String with UTC designator should not be valid as a PlainTime"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-wrong-type.js
new file mode 100644
index 0000000000..dd4a481c5c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-wrong-type.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainTime
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => instance.equals(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainTime, "Temporal.PlainTime, object"],
+ [Temporal.PlainTime.prototype, "Temporal.PlainTime.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.equals(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 0000000000..7fdefea96b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,44 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: Negative time fields are balanced upwards if the argument is given as ZonedDateTime
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-temporal-totemporaltime step 3.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ ii. 1. Set _plainDateTime_ to ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
+ sec-temporal.plaintime.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalTime(_other_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// This code path is encountered if the time zone offset is negative and its
+// absolute value in nanoseconds is greater than the nanosecond field of the
+// exact time's epoch parts
+const tz = TemporalHelpers.specificOffsetTimeZone(-2);
+const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
+
+assert(new Temporal.PlainTime(1, 1, 1, 1, 0, 999).equals(datetime));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..022a102f3e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const instance = new Temporal.PlainTime(16, 50, 35, 0, 0, 1);
+const result = instance.equals(datetime);
+assert.sameValue(result, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..b59852bc69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => time.equals(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..f6ab478702
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => time.equals(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..753983489c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => time.equals(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..b7af51bb67
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => time.equals(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/basic.js
new file mode 100644
index 0000000000..7f9a88ca8f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/basic.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: Basic tests for equals()
+features: [Temporal]
+---*/
+
+const t1 = Temporal.PlainTime.from("08:44:15.321");
+const t1bis = Temporal.PlainTime.from("08:44:15.321");
+const t2 = Temporal.PlainTime.from("14:23:30.123");
+assert.sameValue(t1.equals(t1), true, "same object");
+assert.sameValue(t1.equals(t1bis), true, "different object");
+assert.sameValue(t1.equals(t2), false, "different times");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/branding.js
new file mode 100644
index 0000000000..130ebfd20f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const equals = Temporal.PlainTime.prototype.equals;
+
+assert.sameValue(typeof equals, "function");
+
+const args = [new Temporal.PlainTime(12)];
+
+assert.throws(TypeError, () => equals.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => equals.apply(null, args), "null");
+assert.throws(TypeError, () => equals.apply(true, args), "true");
+assert.throws(TypeError, () => equals.apply("", args), "empty string");
+assert.throws(TypeError, () => equals.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => equals.apply(1, args), "1");
+assert.throws(TypeError, () => equals.apply({}, args), "plain object");
+assert.throws(TypeError, () => equals.apply(Temporal.PlainTime, args), "Temporal.PlainTime");
+assert.throws(TypeError, () => equals.apply(Temporal.PlainTime.prototype, args), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/builtin.js
new file mode 100644
index 0000000000..24538a40b9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: >
+ Tests that Temporal.PlainTime.prototype.equals
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainTime.prototype.equals),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.prototype.equals),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.prototype.equals),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.prototype.equals.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/leap-second.js
new file mode 100644
index 0000000000..f2e3800021
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/leap-second.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: Leap second is a valid ISO string for PlainTime
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(23, 59, 59);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.equals(arg);
+assert.sameValue(
+ result1,
+ true,
+ "leap second is a valid ISO string for PlainTime"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.equals(arg);
+assert.sameValue(
+ result2,
+ true,
+ "second: 60 is ignored in property bag for PlainTime"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/length.js
new file mode 100644
index 0000000000..754520f733
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: Temporal.PlainTime.prototype.equals.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.equals, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/name.js
new file mode 100644
index 0000000000..3861880ffb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: Temporal.PlainTime.prototype.equals.name is "equals".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.equals, "name", {
+ value: "equals",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/not-a-constructor.js
new file mode 100644
index 0000000000..665cb307aa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: >
+ Temporal.PlainTime.prototype.equals does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainTime.prototype.equals();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.prototype.equals), false,
+ "isConstructor(Temporal.PlainTime.prototype.equals)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/plaintime-propertybag-no-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/plaintime-propertybag-no-time-units.js
new file mode 100644
index 0000000000..46d372ec5a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/plaintime-propertybag-no-time-units.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: Missing time units in property bag default to 0
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(0, 30, 0, 0, 0, 0);
+
+const props = {};
+assert.throws(TypeError, () => instance.equals(props), "TypeError if no properties are present");
+
+props.minute = 30;
+const result = instance.equals(props);
+assert.sameValue(result, true, "missing time units default to 0");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/prop-desc.js
new file mode 100644
index 0000000000..377b8bde6f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: The "equals" property of Temporal.PlainTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.prototype.equals,
+ "function",
+ "`typeof PlainTime.prototype.equals` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime.prototype, "equals", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/year-zero.js
new file mode 100644
index 0000000000..160eae9767
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/year-zero.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.equals
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-12-07T03:24:30",
+ "-000000-12-07T03:24:30+01:00",
+ "-000000-12-07T03:24:30+00:00[UTC]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/branding.js
new file mode 100644
index 0000000000..d363874fc6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.getisofields
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const getISOFields = Temporal.PlainTime.prototype.getISOFields;
+
+assert.sameValue(typeof getISOFields, "function");
+
+assert.throws(TypeError, () => getISOFields.call(undefined), "undefined");
+assert.throws(TypeError, () => getISOFields.call(null), "null");
+assert.throws(TypeError, () => getISOFields.call(true), "true");
+assert.throws(TypeError, () => getISOFields.call(""), "empty string");
+assert.throws(TypeError, () => getISOFields.call(Symbol()), "symbol");
+assert.throws(TypeError, () => getISOFields.call(1), "1");
+assert.throws(TypeError, () => getISOFields.call({}), "plain object");
+assert.throws(TypeError, () => getISOFields.call(Temporal.PlainTime), "Temporal.PlainTime");
+assert.throws(TypeError, () => getISOFields.call(Temporal.PlainTime.prototype), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/builtin.js
new file mode 100644
index 0000000000..d0535e6c9a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.getisofields
+description: >
+ Tests that Temporal.PlainTime.prototype.getISOFields
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainTime.prototype.getISOFields),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.prototype.getISOFields),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.prototype.getISOFields),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.prototype.getISOFields.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/field-names.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/field-names.js
new file mode 100644
index 0000000000..efab816b91
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/field-names.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.getisofields
+description: Correct field names on the object returned from getISOFields
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const result = time.getISOFields();
+assert.sameValue(result.isoHour, 12, "isoHour result");
+assert.sameValue(result.isoMinute, 34, "isoMinute result");
+assert.sameValue(result.isoSecond, 56, "isoSecond result");
+assert.sameValue(result.isoMillisecond, 987, "isoMillisecond result");
+assert.sameValue(result.isoMicrosecond, 654, "isoMicrosecond result");
+assert.sameValue(result.isoNanosecond, 321, "isoNanosecond result");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/field-prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/field-prop-desc.js
new file mode 100644
index 0000000000..8c7f4fd3e8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/field-prop-desc.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.getisofields
+description: Properties on the returned object have the correct descriptor
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "isoHour",
+ "isoMicrosecond",
+ "isoMillisecond",
+ "isoMinute",
+ "isoNanosecond",
+ "isoSecond",
+];
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const result = time.getISOFields();
+
+for (const property of expected) {
+ verifyProperty(result, property, {
+ writable: true,
+ enumerable: true,
+ configurable: true,
+ });
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/field-traversal-order.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/field-traversal-order.js
new file mode 100644
index 0000000000..7c8e654af0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/field-traversal-order.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.getisofields
+description: Properties added in correct order to object returned from getISOFields
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "isoHour",
+ "isoMicrosecond",
+ "isoMillisecond",
+ "isoMinute",
+ "isoNanosecond",
+ "isoSecond",
+];
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const result = time.getISOFields();
+
+assert.compareArray(Object.keys(result), expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/length.js
new file mode 100644
index 0000000000..2732f4a9df
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.getisofields
+description: Temporal.PlainTime.prototype.getISOFields.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.getISOFields, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/name.js
new file mode 100644
index 0000000000..a7c83c1466
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.getisofields
+description: Temporal.PlainTime.prototype.getISOFields.name is "getISOFields".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.getISOFields, "name", {
+ value: "getISOFields",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/not-a-constructor.js
new file mode 100644
index 0000000000..057ed2c4fc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.getisofields
+description: >
+ Temporal.PlainTime.prototype.getISOFields does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainTime.prototype.getISOFields();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.prototype.getISOFields), false,
+ "isConstructor(Temporal.PlainTime.prototype.getISOFields)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/prop-desc.js
new file mode 100644
index 0000000000..c530cf5adb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.getisofields
+description: The "getISOFields" property of Temporal.PlainTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.prototype.getISOFields,
+ "function",
+ "`typeof PlainTime.prototype.getISOFields` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime.prototype, "getISOFields", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/prototype.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/prototype.js
new file mode 100644
index 0000000000..086916464a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/prototype.js
@@ -0,0 +1,15 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.getisofields
+description: Correct prototype on the object returned from getISOFields
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const result = instance.getISOFields();
+assert.sameValue(Object.getPrototypeOf(result), Object.prototype, "prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/branding.js
new file mode 100644
index 0000000000..0329ac3688
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaintime.prototype.hour
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const hour = Object.getOwnPropertyDescriptor(Temporal.PlainTime.prototype, "hour").get;
+
+assert.sameValue(typeof hour, "function");
+
+assert.throws(TypeError, () => hour.call(undefined), "undefined");
+assert.throws(TypeError, () => hour.call(null), "null");
+assert.throws(TypeError, () => hour.call(true), "true");
+assert.throws(TypeError, () => hour.call(""), "empty string");
+assert.throws(TypeError, () => hour.call(Symbol()), "symbol");
+assert.throws(TypeError, () => hour.call(1), "1");
+assert.throws(TypeError, () => hour.call({}), "plain object");
+assert.throws(TypeError, () => hour.call(Temporal.PlainTime), "Temporal.PlainTime");
+assert.throws(TypeError, () => hour.call(Temporal.PlainTime.prototype), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/prop-desc.js
new file mode 100644
index 0000000000..2a54f27891
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaintime.prototype.hour
+description: The "hour" property of Temporal.PlainTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainTime.prototype, "hour");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/branding.js
new file mode 100644
index 0000000000..412d16146a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaintime.prototype.microsecond
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const microsecond = Object.getOwnPropertyDescriptor(Temporal.PlainTime.prototype, "microsecond").get;
+
+assert.sameValue(typeof microsecond, "function");
+
+assert.throws(TypeError, () => microsecond.call(undefined), "undefined");
+assert.throws(TypeError, () => microsecond.call(null), "null");
+assert.throws(TypeError, () => microsecond.call(true), "true");
+assert.throws(TypeError, () => microsecond.call(""), "empty string");
+assert.throws(TypeError, () => microsecond.call(Symbol()), "symbol");
+assert.throws(TypeError, () => microsecond.call(1), "1");
+assert.throws(TypeError, () => microsecond.call({}), "plain object");
+assert.throws(TypeError, () => microsecond.call(Temporal.PlainTime), "Temporal.PlainTime");
+assert.throws(TypeError, () => microsecond.call(Temporal.PlainTime.prototype), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/prop-desc.js
new file mode 100644
index 0000000000..d05750e737
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaintime.prototype.microsecond
+description: The "microsecond" property of Temporal.PlainTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainTime.prototype, "microsecond");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/branding.js
new file mode 100644
index 0000000000..18c94ea454
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaintime.prototype.millisecond
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const millisecond = Object.getOwnPropertyDescriptor(Temporal.PlainTime.prototype, "millisecond").get;
+
+assert.sameValue(typeof millisecond, "function");
+
+assert.throws(TypeError, () => millisecond.call(undefined), "undefined");
+assert.throws(TypeError, () => millisecond.call(null), "null");
+assert.throws(TypeError, () => millisecond.call(true), "true");
+assert.throws(TypeError, () => millisecond.call(""), "empty string");
+assert.throws(TypeError, () => millisecond.call(Symbol()), "symbol");
+assert.throws(TypeError, () => millisecond.call(1), "1");
+assert.throws(TypeError, () => millisecond.call({}), "plain object");
+assert.throws(TypeError, () => millisecond.call(Temporal.PlainTime), "Temporal.PlainTime");
+assert.throws(TypeError, () => millisecond.call(Temporal.PlainTime.prototype), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/prop-desc.js
new file mode 100644
index 0000000000..cda2bc0bea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaintime.prototype.millisecond
+description: The "millisecond" property of Temporal.PlainTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainTime.prototype, "millisecond");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/branding.js
new file mode 100644
index 0000000000..b981434142
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaintime.prototype.minute
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const minute = Object.getOwnPropertyDescriptor(Temporal.PlainTime.prototype, "minute").get;
+
+assert.sameValue(typeof minute, "function");
+
+assert.throws(TypeError, () => minute.call(undefined), "undefined");
+assert.throws(TypeError, () => minute.call(null), "null");
+assert.throws(TypeError, () => minute.call(true), "true");
+assert.throws(TypeError, () => minute.call(""), "empty string");
+assert.throws(TypeError, () => minute.call(Symbol()), "symbol");
+assert.throws(TypeError, () => minute.call(1), "1");
+assert.throws(TypeError, () => minute.call({}), "plain object");
+assert.throws(TypeError, () => minute.call(Temporal.PlainTime), "Temporal.PlainTime");
+assert.throws(TypeError, () => minute.call(Temporal.PlainTime.prototype), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/prop-desc.js
new file mode 100644
index 0000000000..c64a296a50
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaintime.prototype.minute
+description: The "minute" property of Temporal.PlainTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainTime.prototype, "minute");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/branding.js
new file mode 100644
index 0000000000..04da9afca7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaintime.prototype.nanosecond
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const nanosecond = Object.getOwnPropertyDescriptor(Temporal.PlainTime.prototype, "nanosecond").get;
+
+assert.sameValue(typeof nanosecond, "function");
+
+assert.throws(TypeError, () => nanosecond.call(undefined), "undefined");
+assert.throws(TypeError, () => nanosecond.call(null), "null");
+assert.throws(TypeError, () => nanosecond.call(true), "true");
+assert.throws(TypeError, () => nanosecond.call(""), "empty string");
+assert.throws(TypeError, () => nanosecond.call(Symbol()), "symbol");
+assert.throws(TypeError, () => nanosecond.call(1), "1");
+assert.throws(TypeError, () => nanosecond.call({}), "plain object");
+assert.throws(TypeError, () => nanosecond.call(Temporal.PlainTime), "Temporal.PlainTime");
+assert.throws(TypeError, () => nanosecond.call(Temporal.PlainTime.prototype), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/prop-desc.js
new file mode 100644
index 0000000000..9b47e79e69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaintime.prototype.nanosecond
+description: The "nanosecond" property of Temporal.PlainTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainTime.prototype, "nanosecond");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/prop-desc.js
new file mode 100644
index 0000000000..3e99b4fe25
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/prop-desc.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-plaintime-prototype
+description: The "prototype" property of Temporal.PlainTime
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(typeof Temporal.PlainTime.prototype, "object");
+assert.notSameValue(Temporal.PlainTime.prototype, null);
+
+verifyProperty(Temporal.PlainTime, "prototype", {
+ writable: false,
+ enumerable: false,
+ configurable: false,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/branding.js
new file mode 100644
index 0000000000..97588eae2e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const round = Temporal.PlainTime.prototype.round;
+
+assert.sameValue(typeof round, "function");
+
+const args = ["hour"];
+
+assert.throws(TypeError, () => round.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => round.apply(null, args), "null");
+assert.throws(TypeError, () => round.apply(true, args), "true");
+assert.throws(TypeError, () => round.apply("", args), "empty string");
+assert.throws(TypeError, () => round.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => round.apply(1, args), "1");
+assert.throws(TypeError, () => round.apply({}, args), "plain object");
+assert.throws(TypeError, () => round.apply(Temporal.PlainTime, args), "Temporal.PlainTime");
+assert.throws(TypeError, () => round.apply(Temporal.PlainTime.prototype, args), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/builtin.js
new file mode 100644
index 0000000000..ac46ab8fc4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: >
+ Tests that Temporal.PlainTime.prototype.round
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainTime.prototype.round),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.prototype.round),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.prototype.round),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.prototype.round.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/length.js
new file mode 100644
index 0000000000..f4ff8fb86e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Temporal.PlainTime.prototype.round.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.round, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/name.js
new file mode 100644
index 0000000000..4f6f5b4c87
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Temporal.PlainTime.prototype.round.name is "round".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.round, "name", {
+ value: "round",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/not-a-constructor.js
new file mode 100644
index 0000000000..591cc2d3a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: >
+ Temporal.PlainTime.prototype.round does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainTime.prototype.round();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.prototype.round), false,
+ "isConstructor(Temporal.PlainTime.prototype.round)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/options-wrong-type.js
new file mode 100644
index 0000000000..c576d9cebd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/options-wrong-type.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: TypeError thrown when options argument is missing or a non-string primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ undefined,
+ null,
+ true,
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainTime();
+assert.throws(TypeError, () => instance.round(), "TypeError on missing options argument");
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.round(value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/prop-desc.js
new file mode 100644
index 0000000000..13c7270880
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: The "round" property of Temporal.PlainTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.prototype.round,
+ "function",
+ "`typeof PlainTime.prototype.round` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime.prototype, "round", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/rounding-cross-midnight.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/rounding-cross-midnight.js
new file mode 100644
index 0000000000..294ba531ed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/rounding-cross-midnight.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Rounding can cross midnight
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = Temporal.PlainTime.from("23:59:59.999999999");
+for (const smallestUnit of ["hour", "minute", "second", "millisecond", "microsecond"]) {
+ TemporalHelpers.assertPlainTime(plainTime.round({ smallestUnit }), 0, 0, 0, 0, 0, 0);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-hours.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-hours.js
new file mode 100644
index 0000000000..ec7cbe2b56
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-hours.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(3, 34, 56, 987, 654, 321);
+
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "hours", roundingIncrement: 1 }),
+ 4, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "hours", roundingIncrement: 2 }),
+ 4, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "hours", roundingIncrement: 3 }),
+ 3, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "hours", roundingIncrement: 4 }),
+ 4, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "hours", roundingIncrement: 6 }),
+ 6, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "hours", roundingIncrement: 8 }),
+ 0, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "hours", roundingIncrement: 12 }),
+ 0, 0, 0, 0, 0, 0, "hours");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-invalid.js
new file mode 100644
index 0000000000..46eab63aa7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-invalid.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Tests roundingIncrement restrictions.
+features: [Temporal]
+---*/
+
+const plainTime = Temporal.PlainTime.from("08:22:36.123456789");
+
+assert.throws(RangeError, () => plainTime.round({ smallestUnit: "hours", roundingIncrement: 11 }));
+assert.throws(RangeError, () => plainTime.round({ smallestUnit: "minutes", roundingIncrement: 29 }));
+assert.throws(RangeError, () => plainTime.round({ smallestUnit: "seconds", roundingIncrement: 29 }));
+assert.throws(RangeError, () => plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 29 }));
+assert.throws(RangeError, () => plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 29 }));
+assert.throws(RangeError, () => plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 29 }));
+assert.throws(RangeError, () => plainTime.round({ smallestUnit: "hours", roundingIncrement: 24 }));
+assert.throws(RangeError, () => plainTime.round({ smallestUnit: "minutes", roundingIncrement: 60 }));
+assert.throws(RangeError, () => plainTime.round({ smallestUnit: "seconds", roundingIncrement: 60 }));
+assert.throws(RangeError, () => plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 1000 }));
+assert.throws(RangeError, () => plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 1000 }));
+assert.throws(RangeError, () => plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 1000 }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-microseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-microseconds.js
new file mode 100644
index 0000000000..20d4a13ded
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-microseconds.js
@@ -0,0 +1,60 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(3, 34, 56, 987, 654, 321);
+
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 1 }),
+ 3, 34, 56, 987, 654, 0, "microseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 2 }),
+ 3, 34, 56, 987, 654, 0, "microseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 4 }),
+ 3, 34, 56, 987, 656, 0, "microseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 5 }),
+ 3, 34, 56, 987, 655, 0, "microseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 8 }),
+ 3, 34, 56, 987, 656, 0, "microseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 10 }),
+ 3, 34, 56, 987, 650, 0, "microseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 20 }),
+ 3, 34, 56, 987, 660, 0, "microseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 25 }),
+ 3, 34, 56, 987, 650, 0, "microseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 40 }),
+ 3, 34, 56, 987, 640, 0, "microseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 50 }),
+ 3, 34, 56, 987, 650, 0, "microseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 100 }),
+ 3, 34, 56, 987, 700, 0, "microseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 125 }),
+ 3, 34, 56, 987, 625, 0, "microseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 200 }),
+ 3, 34, 56, 987, 600, 0, "microseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 250 }),
+ 3, 34, 56, 987, 750, 0, "microseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 500 }),
+ 3, 34, 56, 987, 500, 0, "microseconds");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-milliseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-milliseconds.js
new file mode 100644
index 0000000000..e84da703b2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-milliseconds.js
@@ -0,0 +1,60 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(3, 34, 56, 987, 654, 321);
+
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 1 }),
+ 3, 34, 56, 988, 0, 0, "milliseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 2 }),
+ 3, 34, 56, 988, 0, 0, "milliseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 4 }),
+ 3, 34, 56, 988, 0, 0, "milliseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 5 }),
+ 3, 34, 56, 990, 0, 0, "milliseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 8 }),
+ 3, 34, 56, 984, 0, 0, "milliseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 10 }),
+ 3, 34, 56, 990, 0, 0, "milliseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 20 }),
+ 3, 34, 56, 980, 0, 0, "milliseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 25 }),
+ 3, 34, 57, 0, 0, 0, "milliseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 40 }),
+ 3, 34, 57, 0, 0, 0, "milliseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 50 }),
+ 3, 34, 57, 0, 0, 0, "milliseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 100 }),
+ 3, 34, 57, 0, 0, 0, "milliseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 125 }),
+ 3, 34, 57, 0, 0, 0, "milliseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 200 }),
+ 3, 34, 57, 0, 0, 0, "milliseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 250 }),
+ 3, 34, 57, 0, 0, 0, "milliseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 500 }),
+ 3, 34, 57, 0, 0, 0, "milliseconds");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-minutes.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-minutes.js
new file mode 100644
index 0000000000..88b8a73dd1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-minutes.js
@@ -0,0 +1,48 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(3, 34, 56, 987, 654, 321);
+
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "minutes", roundingIncrement: 1 }),
+ 3, 35, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "minutes", roundingIncrement: 2 }),
+ 3, 34, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "minutes", roundingIncrement: 3 }),
+ 3, 36, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "minutes", roundingIncrement: 4 }),
+ 3, 36, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "minutes", roundingIncrement: 5 }),
+ 3, 35, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "minutes", roundingIncrement: 6 }),
+ 3, 36, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "minutes", roundingIncrement: 10 }),
+ 3, 30, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "minutes", roundingIncrement: 12 }),
+ 3, 36, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "minutes", roundingIncrement: 15 }),
+ 3, 30, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "minutes", roundingIncrement: 20 }),
+ 3, 40, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "minutes", roundingIncrement: 30 }),
+ 3, 30, 0, 0, 0, 0, "minutes");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-nan.js
new file mode 100644
index 0000000000..c1874a2bd7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-nan.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: RangeError thrown when roundingIncrement option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaintime.prototype.round step 11:
+ 10. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+assert.throws(RangeError, () => time.round({ smallestUnit: 'second', roundingIncrement: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-nanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-nanoseconds.js
new file mode 100644
index 0000000000..9b6a1c8f77
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-nanoseconds.js
@@ -0,0 +1,60 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(3, 34, 56, 987, 654, 321);
+
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 1 }),
+ 3, 34, 56, 987, 654, 321, "nanoseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 2 }),
+ 3, 34, 56, 987, 654, 322, "nanoseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 4 }),
+ 3, 34, 56, 987, 654, 320, "nanoseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 5 }),
+ 3, 34, 56, 987, 654, 320, "nanoseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 8 }),
+ 3, 34, 56, 987, 654, 320, "nanoseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 10 }),
+ 3, 34, 56, 987, 654, 320, "nanoseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 20 }),
+ 3, 34, 56, 987, 654, 320, "nanoseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 25 }),
+ 3, 34, 56, 987, 654, 325, "nanoseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 40 }),
+ 3, 34, 56, 987, 654, 320, "nanoseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 50 }),
+ 3, 34, 56, 987, 654, 300, "nanoseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 100 }),
+ 3, 34, 56, 987, 654, 300, "nanoseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 125 }),
+ 3, 34, 56, 987, 654, 375, "nanoseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 200 }),
+ 3, 34, 56, 987, 654, 400, "nanoseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 250 }),
+ 3, 34, 56, 987, 654, 250, "nanoseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 500 }),
+ 3, 34, 56, 987, 654, 500, "nanoseconds");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-non-integer.js
new file mode 100644
index 0000000000..e70e20ea85
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-non-integer.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Rounding for roundingIncrement option
+info: |
+ ToTemporalRoundingIncrement ( _normalizedOptions_ )
+
+ 1. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, *"number"*, *undefined*, *1*<sub>𝔽</sub>).
+ 2. If _increment_ is not finite, throw a *RangeError* exception.
+ 3. Let _integerIncrement_ be truncate(ℝ(_increment_)).
+ 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception.
+ 5. Return _integerIncrement_.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 0, 0, 5);
+const result = time.round({ smallestUnit: "nanosecond", roundingIncrement: 2.5, roundingMode: "expand" });
+TemporalHelpers.assertPlainTime(result, 12, 34, 56, 0, 0, 6, "roundingIncrement 2.5 truncates to 2");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-out-of-range.js
new file mode 100644
index 0000000000..832bae5f60
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-out-of-range.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: RangeError thrown when roundingIncrement option out of range
+info: |
+ ToTemporalRoundingIncrement ( _normalizedOptions_ )
+
+ 1. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, *"number"*, *undefined*, *1*<sub>𝔽</sub>).
+ 2. If _increment_ is not finite, throw a *RangeError* exception.
+ 3. Let _integerIncrement_ be truncate(ℝ(_increment_)).
+ 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception.
+ 5. Return _integerIncrement_.
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 0, 0, 5);
+assert.throws(RangeError, () => time.round({ smallestUnit: "nanoseconds", roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => time.round({ smallestUnit: "nanoseconds", roundingIncrement: -1 }));
+assert.throws(RangeError, () => time.round({ smallestUnit: "nanoseconds", roundingIncrement: 0 }));
+assert.throws(RangeError, () => time.round({ smallestUnit: "nanoseconds", roundingIncrement: 0.9 }));
+assert.throws(RangeError, () => time.round({ smallestUnit: "nanoseconds", roundingIncrement: 1e9 + 1 }));
+assert.throws(RangeError, () => time.round({ smallestUnit: "nanoseconds", roundingIncrement: Infinity }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-seconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-seconds.js
new file mode 100644
index 0000000000..342bccbf37
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-seconds.js
@@ -0,0 +1,48 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(3, 34, 56, 987, 654, 321);
+
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "seconds", roundingIncrement: 1 }),
+ 3, 34, 57, 0, 0, 0, "seconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "seconds", roundingIncrement: 2 }),
+ 3, 34, 56, 0, 0, 0, "seconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "seconds", roundingIncrement: 3 }),
+ 3, 34, 57, 0, 0, 0, "seconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "seconds", roundingIncrement: 4 }),
+ 3, 34, 56, 0, 0, 0, "seconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "seconds", roundingIncrement: 5 }),
+ 3, 34, 55, 0, 0, 0, "seconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "seconds", roundingIncrement: 6 }),
+ 3, 34, 54, 0, 0, 0, "seconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "seconds", roundingIncrement: 10 }),
+ 3, 35, 0, 0, 0, 0, "seconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "seconds", roundingIncrement: 12 }),
+ 3, 35, 0, 0, 0, 0, "seconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "seconds", roundingIncrement: 15 }),
+ 3, 35, 0, 0, 0, 0, "seconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "seconds", roundingIncrement: 20 }),
+ 3, 35, 0, 0, 0, 0, "seconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "seconds", roundingIncrement: 30 }),
+ 3, 35, 0, 0, 0, 0, "seconds");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-undefined.js
new file mode 100644
index 0000000000..a587e8c163
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-undefined.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Fallback value for roundingIncrement option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaintime.prototype.round step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const explicit = time.round({ smallestUnit: 'second', roundingIncrement: undefined });
+TemporalHelpers.assertPlainTime(explicit, 12, 34, 57, 0, 0, 0, "default roundingIncrement is 1");
+
+const implicit = time.round({ smallestUnit: 'second' });
+TemporalHelpers.assertPlainTime(implicit, 12, 34, 57, 0, 0, 0, "default roundingIncrement is 1");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-wrong-type.js
new file mode 100644
index 0000000000..fa9fbc8404
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-wrong-type.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Type conversions for roundingIncrement option
+info: |
+ sec-getoption step 8.a:
+ a. Set _value_ to ? ToNumber(value).
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaintime.prototype.round step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => time.round({ smallestUnit: 'second', roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertPlainTime(result, 12, 34, 57, 0, 0, 0, descr),
+ (result, descr) => TemporalHelpers.assertPlainTime(result, 12, 34, 56, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-ceil.js
new file mode 100644
index 0000000000..022e5156f0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-ceil.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Tests calculations with roundingMode "ceil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(13, 46, 23, 123, 987, 500);
+
+const expected = [
+ ["hour", [14]],
+ ["minute", [13, 47]],
+ ["second", [13, 46, 24]],
+ ["millisecond", [13, 46, 23, 124]],
+ ["microsecond", [13, 46, 23, 123, 988]],
+ ["nanosecond", [13, 46, 23, 123, 987, 500]],
+];
+
+const roundingMode = "ceil";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [h, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainTime(
+ instance.round({ smallestUnit, roundingMode }),
+ h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-expand.js
new file mode 100644
index 0000000000..2cfffad15d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-expand.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Tests calculations with roundingMode "expand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(13, 46, 23, 123, 987, 500);
+
+const expected = [
+ ["hour", [14]],
+ ["minute", [13, 47]],
+ ["second", [13, 46, 24]],
+ ["millisecond", [13, 46, 23, 124]],
+ ["microsecond", [13, 46, 23, 123, 988]],
+ ["nanosecond", [13, 46, 23, 123, 987, 500]],
+];
+
+const roundingMode = "expand";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [h, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainTime(
+ instance.round({ smallestUnit, roundingMode }),
+ h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-floor.js
new file mode 100644
index 0000000000..e8616991f2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-floor.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Tests calculations with roundingMode "floor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(13, 46, 23, 123, 987, 500);
+
+const expected = [
+ ["hour", [13]],
+ ["minute", [13, 46]],
+ ["second", [13, 46, 23]],
+ ["millisecond", [13, 46, 23, 123]],
+ ["microsecond", [13, 46, 23, 123, 987]],
+ ["nanosecond", [13, 46, 23, 123, 987, 500]],
+];
+
+const roundingMode = "floor";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [h, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainTime(
+ instance.round({ smallestUnit, roundingMode }),
+ h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..d026326ec0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfCeil.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Tests calculations with roundingMode "halfCeil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(13, 46, 23, 123, 987, 500);
+
+const expected = [
+ ["hour", [14]],
+ ["minute", [13, 46]],
+ ["second", [13, 46, 23]],
+ ["millisecond", [13, 46, 23, 124]],
+ ["microsecond", [13, 46, 23, 123, 988]],
+ ["nanosecond", [13, 46, 23, 123, 987, 500]],
+];
+
+const roundingMode = "halfCeil";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [h, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainTime(
+ instance.round({ smallestUnit, roundingMode }),
+ h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfEven.js
new file mode 100644
index 0000000000..1b1896efe1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfEven.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Tests calculations with roundingMode "halfEven".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(13, 46, 23, 123, 987, 500);
+
+const expected = [
+ ["hour", [14]],
+ ["minute", [13, 46]],
+ ["second", [13, 46, 23]],
+ ["millisecond", [13, 46, 23, 124]],
+ ["microsecond", [13, 46, 23, 123, 988]],
+ ["nanosecond", [13, 46, 23, 123, 987, 500]],
+];
+
+const roundingMode = "halfEven";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [h, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainTime(
+ instance.round({ smallestUnit, roundingMode }),
+ h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..c0f93790fb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfExpand.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Tests calculations with roundingMode "halfExpand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(13, 46, 23, 123, 987, 500);
+
+const expected = [
+ ["hour", [14]],
+ ["minute", [13, 46]],
+ ["second", [13, 46, 23]],
+ ["millisecond", [13, 46, 23, 124]],
+ ["microsecond", [13, 46, 23, 123, 988]],
+ ["nanosecond", [13, 46, 23, 123, 987, 500]],
+];
+
+const roundingMode = "halfExpand";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [h, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainTime(
+ instance.round({ smallestUnit, roundingMode }),
+ h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..0bfe52baff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfFloor.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Tests calculations with roundingMode "halfFloor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(13, 46, 23, 123, 987, 500);
+
+const expected = [
+ ["hour", [14]],
+ ["minute", [13, 46]],
+ ["second", [13, 46, 23]],
+ ["millisecond", [13, 46, 23, 124]],
+ ["microsecond", [13, 46, 23, 123, 987]],
+ ["nanosecond", [13, 46, 23, 123, 987, 500]],
+];
+
+const roundingMode = "halfFloor";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [h, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainTime(
+ instance.round({ smallestUnit, roundingMode }),
+ h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..bcab3dd219
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfTrunc.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Tests calculations with roundingMode "halfTrunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(13, 46, 23, 123, 987, 500);
+
+const expected = [
+ ["hour", [14]],
+ ["minute", [13, 46]],
+ ["second", [13, 46, 23]],
+ ["millisecond", [13, 46, 23, 124]],
+ ["microsecond", [13, 46, 23, 123, 987]],
+ ["nanosecond", [13, 46, 23, 123, 987, 500]],
+];
+
+const roundingMode = "halfTrunc";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [h, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainTime(
+ instance.round({ smallestUnit, roundingMode }),
+ h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..619f34d9fb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-invalid-string.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) {
+ assert.throws(RangeError, () => time.round({ smallestUnit: "microsecond", roundingMode }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-trunc.js
new file mode 100644
index 0000000000..a687834ecc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-trunc.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Tests calculations with roundingMode "trunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(13, 46, 23, 123, 987, 500);
+
+const expected = [
+ ["hour", [13]],
+ ["minute", [13, 46]],
+ ["second", [13, 46, 23]],
+ ["millisecond", [13, 46, 23, 123]],
+ ["microsecond", [13, 46, 23, 123, 987]],
+ ["nanosecond", [13, 46, 23, 123, 987, 500]],
+];
+
+const roundingMode = "trunc";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [h, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainTime(
+ instance.round({ smallestUnit, roundingMode }),
+ h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-undefined.js
new file mode 100644
index 0000000000..8f1d4d44a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-undefined.js
@@ -0,0 +1,56 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Tests calculations with roundingMode undefined.
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const plainTime = Temporal.PlainTime.from("13:46:23.123456789");
+
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "hour", roundingMode: undefined }),
+ 14, 0, 0, 0, 0, 0, "hour");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "hour" }),
+ 14, 0, 0, 0, 0, 0, "hour");
+
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "minute", roundingMode: undefined }),
+ 13, 46, 0, 0, 0, 0, "minute");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "minute" }),
+ 13, 46, 0, 0, 0, 0, "minute");
+
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "second", roundingMode: undefined }),
+ 13, 46, 23, 0, 0, 0, "second");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "second" }),
+ 13, 46, 23, 0, 0, 0, "second");
+
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "millisecond", roundingMode: undefined }),
+ 13, 46, 23, 123, 0, 0, "millisecond");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "millisecond" }),
+ 13, 46, 23, 123, 0, 0, "millisecond");
+
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microsecond", roundingMode: undefined }),
+ 13, 46, 23, 123, 457, 0, "microsecond");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microsecond" }),
+ 13, 46, 23, 123, 457, 0, "microsecond");
+
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanosecond", roundingMode: undefined }),
+ 13, 46, 23, 123, 456, 789, "nanosecond");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanosecond" }),
+ 13, 46, 23, 123, 456, 789, "nanosecond");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..3c8d77339d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-wrong-type.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "halfExpand",
+ (roundingMode) => time.round({ smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => TemporalHelpers.assertPlainTime(result, 12, 34, 56, 123, 988, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundto-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundto-invalid-string.js
new file mode 100644
index 0000000000..90b17f979c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundto-invalid-string.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+const badValues = [
+ "era",
+ "eraYear",
+ "year",
+ "month",
+ "week",
+ "day",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => time.round(smallestUnit),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..23e2b56612
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-invalid-string.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+const badValues = [
+ "era",
+ "eraYear",
+ "year",
+ "month",
+ "week",
+ "day",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => time.round({ smallestUnit }),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-missing.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-missing.js
new file mode 100644
index 0000000000..0a1df6022a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-missing.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: RangeError thrown when smallestUnit option is missing
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+assert.throws(TypeError, () => plainTime.round());
+assert.throws(RangeError, () => plainTime.round({}));
+assert.throws(RangeError, () => plainTime.round({ roundingIncrement: 1, roundingMode: "ceil" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..8cc10926f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-plurals-accepted.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 789, 999, 999);
+const validUnits = [
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => time.round({ smallestUnit }), validUnits);
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => time.round(smallestUnit), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-string-shorthand.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-string-shorthand.js
new file mode 100644
index 0000000000..e3cc87fa1b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-string-shorthand.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: String as first argument is equivalent to options bag with smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 789, 999, 999);
+const validUnits = [
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+validUnits.forEach((smallestUnit) => {
+ const full = instance.round({ smallestUnit });
+ const shorthand = instance.round(smallestUnit);
+ TemporalHelpers.assertPlainTimesEqual(shorthand, full, `"${smallestUnit}" as first argument to round is equivalent to options bag`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..ccaabccf26
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-wrong-type.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => time.round({ smallestUnit }),
+ (result, descr) => TemporalHelpers.assertPlainTime(result, 12, 34, 56, 123, 988, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/subclassing-ignored.js
new file mode 100644
index 0000000000..0b574f0a9d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/subclassing-ignored.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainTime,
+ [12, 34, 56, 987, 654, 321],
+ "round",
+ [{ smallestUnit: 'second' }],
+ (result) => TemporalHelpers.assertPlainTime(result, 12, 34, 57, 0, 0, 0),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/branding.js
new file mode 100644
index 0000000000..aa3af6a146
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaintime.prototype.second
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const second = Object.getOwnPropertyDescriptor(Temporal.PlainTime.prototype, "second").get;
+
+assert.sameValue(typeof second, "function");
+
+assert.throws(TypeError, () => second.call(undefined), "undefined");
+assert.throws(TypeError, () => second.call(null), "null");
+assert.throws(TypeError, () => second.call(true), "true");
+assert.throws(TypeError, () => second.call(""), "empty string");
+assert.throws(TypeError, () => second.call(Symbol()), "symbol");
+assert.throws(TypeError, () => second.call(1), "1");
+assert.throws(TypeError, () => second.call({}), "plain object");
+assert.throws(TypeError, () => second.call(Temporal.PlainTime), "Temporal.PlainTime");
+assert.throws(TypeError, () => second.call(Temporal.PlainTime.prototype), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/prop-desc.js
new file mode 100644
index 0000000000..c4a03e47c4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaintime.prototype.second
+description: The "second" property of Temporal.PlainTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainTime.prototype, "second");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-cast.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-cast.js
new file mode 100644
index 0000000000..b53b7be74b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-cast.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Casts the argument
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const plainTime = new Temporal.PlainTime(15, 23, 30, 123, 456, 789);
+TemporalHelpers.assertDuration(plainTime.since("16:34"),
+ 0, 0, 0, 0, /* hours = */ -1, /* minutes = */ -10, /* seconds = */ -29, -876, -543, -211, "string");
+TemporalHelpers.assertDuration(plainTime.since({ hour: 16, minute: 34 }),
+ 0, 0, 0, 0, /* hours = */ -1, /* minutes = */ -10, /* seconds = */ -29, -876, -543, -211, "object");
+
+assert.throws(TypeError, () => plainTime.since({}), "empty");
+assert.throws(TypeError, () => plainTime.since({ minutes: 30 }), "only plural 'minutes'");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-number.js
new file mode 100644
index 0000000000..df9cd66d25
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-number.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: A number is invalid in place of an ISO string for Temporal.PlainTime
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const numbers = [
+ 1,
+ -123456.987654321,
+ 1234567,
+ 123456.9876543219,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.since(arg),
+ `A number (${arg}) is not a valid ISO string for PlainTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..ece0c996a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-calendar-annotation.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["12:34:56.987654321[u-ca=iso8601]", "without time zone"],
+ ["12:34:56.987654321[UTC][u-ca=iso8601]", "with time zone"],
+ ["12:34:56.987654321[!u-ca=iso8601]", "with ! and no time zone"],
+ ["12:34:56.987654321[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["T12:34:56.987654321[u-ca=iso8601]", "with T and no time zone"],
+ ["T12:34:56.987654321[UTC][u-ca=iso8601]", "with T and time zone"],
+ ["T12:34:56.987654321[!u-ca=iso8601]", "with T, !, and no time zone"],
+ ["T12:34:56.987654321[UTC][!u-ca=iso8601]", "with T, !, and time zone"],
+ ["1970-01-01T12:34:56.987654321[u-ca=iso8601]", "with date and no time zone"],
+ ["1970-01-01T12:34:56.987654321[UTC][u-ca=iso8601]", "with date and time zone"],
+ ["1970-01-01T12:34:56.987654321[!u-ca=iso8601]", "with !, date, and no time zone"],
+ ["1970-01-01T12:34:56.987654321[UTC][!u-ca=iso8601]", "with !, date, and time zone"],
+ ["12:34:56.987654321[u-ca=hebrew]", "calendar annotation ignored"],
+ ["12:34:56.987654321[u-ca=unknown]", "calendar annotation ignored even if unknown calendar"],
+ ["12:34:56.987654321[!u-ca=unknown]", "calendar annotation ignored even if unknown calendar with !"],
+ ["1970-01-01T12:34:56.987654321[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.since(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..84eb98a585
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "00:00[!foo=bar]",
+ "T00:00[!foo=bar]",
+ "1970-01-01T00:00[!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..83a2cb8f80
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-date-with-utc-offset.js
@@ -0,0 +1,53 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const validStrings = [
+ "12:34:56.987654321+00:00",
+ "12:34:56.987654321+00:00[UTC]",
+ "12:34:56.987654321+00:00[!UTC]",
+ "12:34:56.987654321-02:30[America/St_Johns]",
+ "1976-11-18T12:34:56.987654321+00:00",
+ "1976-11-18T12:34:56.987654321+00:00[UTC]",
+ "1976-11-18T12:34:56.987654321+00:00[!UTC]",
+ "1976-11-18T12:34:56.987654321-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.since(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `"${arg}" is a valid UTC offset with time for PlainTime`
+ );
+}
+
+const invalidStrings = [
+ "2022-09-15Z",
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ `"${arg}" UTC offset without time is not valid for PlainTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..02cb57b271
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-multiple-calendar.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..6a50d1787f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-multiple-time-zone.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "00:00[UTC][UTC]",
+ "T00:00[UTC][UTC]",
+ "1970-01-01T00:00[UTC][UTC]",
+ "1970-01-01T00:00[!UTC][UTC]",
+ "1970-01-01T00:00[UTC][!UTC]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00[UTC][foo=bar][UTC]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-no-implicit-midnight.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-no-implicit-midnight.js
new file mode 100644
index 0000000000..0f4f71046c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-no-implicit-midnight.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: RangeError thrown if a date-only string is passed in a PlainTime context
+features: [Temporal, arrow-function]
+---*/
+
+const arg = "2019-10-01";
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ "Date-only string throws, does not implicitly convert to midnight"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-time-designator-required-for-disambiguation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-time-designator-required-for-disambiguation.js
new file mode 100644
index 0000000000..28da5edc0e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-time-designator-required-for-disambiguation.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: ISO 8601 time designator "T" required in cases of ambiguity
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+TemporalHelpers.ISO.plainTimeStringsAmbiguous().forEach((string) => {
+ let arg = string;
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ `'${arg}' is ambiguous and requires T prefix`
+ );
+ // The same string with a T prefix should not throw:
+ arg = `T${string}`;
+ instance.since(arg);
+
+ arg = ` ${string}`;
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ `space is not accepted as a substitute for T prefix: '${arg}'`
+ );
+});
+
+// None of these should throw without a T prefix, because they are unambiguously time strings:
+TemporalHelpers.ISO.plainTimeStringsUnambiguous().forEach(
+ (arg) => instance.since(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-time-separators.js
new file mode 100644
index 0000000000..252d7a6e53
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-time-separators.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T12:34:56.987654321", "uppercase T"],
+ ["1976-11-18t12:34:56.987654321", "lowercase T"],
+ ["1976-11-18 12:34:56.987654321", "space between date and time"],
+ ["T12:34:56.987654321", "time-only uppercase T"],
+ ["t12:34:56.987654321", "time-only lowercase T"],
+];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.since(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..3f75d12f81
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-time-zone-annotation.js
@@ -0,0 +1,51 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["12:34:56.987654321[Asia/Kolkata]", "named, with no offset"],
+ ["12:34:56.987654321[!Europe/Vienna]", "named, with ! and no offset"],
+ ["12:34:56.987654321[+00:00]", "numeric, with no offset"],
+ ["12:34:56.987654321[!-02:30]", "numeric, with ! and no offset"],
+ ["T12:34:56.987654321[UTC]", "named, with T and no offset"],
+ ["T12:34:56.987654321[!Africa/Abidjan]", "named, with T, !, and no offset"],
+ ["T12:34:56.987654321[+01:00]", "numeric, with T and no offset"],
+ ["T12:34:56.987654321[!-08:00]", "numeric, with T, !, and no offset"],
+ ["12:34:56.987654321+00:00[America/Sao_Paulo]", "named, with offset"],
+ ["12:34:56.987654321+00:00[!Asia/Tokyo]", "named, with ! and offset"],
+ ["12:34:56.987654321+00:00[-02:30]", "numeric, with offset"],
+ ["12:34:56.987654321+00:00[!+00:00]", "numeric, with ! and offset"],
+ ["T12:34:56.987654321+00:00[America/New_York]", "named, with T and offset"],
+ ["T12:34:56.987654321+00:00[!UTC]", "named, with T, !, and offset"],
+ ["T12:34:56.987654321+00:00[-08:00]", "numeric, with T and offset"],
+ ["T12:34:56.987654321+00:00[!+01:00]", "numeric, with T, !, and offset"],
+ ["1970-01-01T12:34:56.987654321[Africa/Lagos]", "named, with date and no offset"],
+ ["1970-01-01T12:34:56.987654321[!America/Vancouver]", "named, with date, !, and no offset"],
+ ["1970-01-01T12:34:56.987654321[+00:00]", "numeric, with date and no offset"],
+ ["1970-01-01T12:34:56.987654321[!-02:30]", "numeric, with date, !, and no offset"],
+ ["1970-01-01T12:34:56.987654321+00:00[Europe/London]", "named, with date and offset"],
+ ["1970-01-01T12:34:56.987654321+00:00[!Asia/Seoul]", "named, with date, offset, and !"],
+ ["1970-01-01T12:34:56.987654321+00:00[+01:00]", "numeric, with date and offset"],
+ ["1970-01-01T12:34:56.987654321+00:00[!-08:00]", "numeric, with date, offset, and !"],
+];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.since(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..1c610e5c72
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-unknown-annotation.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["12:34:56.987654321[foo=bar]", "alone"],
+ ["12:34:56.987654321[UTC][foo=bar]", "with time zone"],
+ ["12:34:56.987654321[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["T12:34:56.987654321[foo=bar]", "with T"],
+ ["T12:34:56.987654321[UTC][foo=bar]", "with T and time zone"],
+ ["T12:34:56.987654321[u-ca=iso8601][foo=bar]", "with T and calendar"],
+ ["T12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with T, time zone, and calendar"],
+ ["1970-01-01T12:34:56.987654321[foo=bar]", "with date"],
+ ["1970-01-01T12:34:56.987654321[UTC][foo=bar]", "with date and time zone"],
+ ["1970-01-01T12:34:56.987654321[u-ca=iso8601][foo=bar]", "with date and calendar"],
+ ["1970-01-01T12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with date, time zone, and calendar"],
+ ["1970-01-01T12:34:56.987654321[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.since(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-with-time-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-with-time-designator.js
new file mode 100644
index 0000000000..6a1ac52bfb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-with-time-designator.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: ISO 8601 time designator "T" allowed at the start of PlainTime strings
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.PlainTime(1, 0, 0, 0, 0, 1);
+const validStrings = [
+ "T00:30",
+ "t00:30",
+ "T0030",
+ "t0030",
+ "T00:30:00",
+ "t00:30:00",
+ "T003000",
+ "t003000",
+ "T00:30:00.000000000",
+ "t00:30:00.000000000",
+ "T003000.000000000",
+ "t003000.000000000",
+];
+validStrings.forEach((arg) => {
+ const result = instance.since(arg);
+ TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 30, 0, 0, 0, 1, `T prefix is accepted: ${arg}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..c7325625ae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-with-utc-designator.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: RangeError thrown if a string with UTC designator is used as a PlainTime
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+ "09:00:00Z[UTC]",
+ "09:00:00Z",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ "String with UTC designator should not be valid as a PlainTime"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-wrong-type.js
new file mode 100644
index 0000000000..7aaa3324f5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-wrong-type.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainTime
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => instance.since(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainTime, "Temporal.PlainTime, object"],
+ [Temporal.PlainTime.prototype, "Temporal.PlainTime.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.since(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 0000000000..ad80b7ee57
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,46 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Negative time fields are balanced upwards if the argument is given as ZonedDateTime
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-temporal-totemporaltime step 3.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ ii. 1. Set _plainDateTime_ to ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
+ sec-temporal.plaintime.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalTime(_other_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// This code path is encountered if the time zone offset is negative and its
+// absolute value in nanoseconds is greater than the nanosecond field of the
+// exact time's epoch parts
+const tz = TemporalHelpers.specificOffsetTimeZone(-2);
+const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
+
+const diff = new Temporal.PlainTime().since(datetime);
+
+TemporalHelpers.assertDuration(diff, 0, 0, 0, 0, -1, -1, -1, -1, 0, -999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..5edc564f07
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const instance = new Temporal.PlainTime(15);
+const result = instance.since(datetime);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, -1, -50, -35, 0, 0, -1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..0b73484ed0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => time.since(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..a977d4f8c7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => time.since(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..1c5998e0bd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => time.since(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..cec6b03fac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => time.since(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/balance-negative-time-units.js
new file mode 100644
index 0000000000..78b3f45f33
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/balance-negative-time-units.js
@@ -0,0 +1,51 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-differencetime step 8:
+ 8. Let _bt_ be ? BalanceTime(_hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
+ sec-temporal.plaintime.prototype.since step 12:
+ 12. Let _result_ be ! DifferenceTime(_other_.[[ISOHour]], _other_.[[ISOMinute]], _other_.[[ISOSecond]], _other_.[[ISOMillisecond]], _other_.[[ISOMicrosecond]], _other_.[[ISONanosecond]], _temporalTime_.[[ISOHour]], _temporalTime_.[[ISOMinute]], _temporalTime_.[[ISOSecond]], _temporalTime_.[[ISOMillisecond]], _temporalTime_.[[ISOMicrosecond]], _temporalTime_.[[ISONanosecond]]).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(1, 1, 1, 1, 1, 1);
+
+const result1 = time.since(new Temporal.PlainTime(0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
+
+const result2 = time.since(new Temporal.PlainTime(0, 0, 0, 0, 2));
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 1, 1, 1, 0, 999, 1, "microseconds balance");
+
+const result3 = time.since(new Temporal.PlainTime(0, 0, 0, 2));
+TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 1, 1, 0, 999, 1, 1, "milliseconds balance");
+
+const result4 = time.since(new Temporal.PlainTime(0, 0, 2));
+TemporalHelpers.assertDuration(result4, 0, 0, 0, 0, 1, 0, 59, 1, 1, 1, "seconds balance");
+
+const result5 = time.since(new Temporal.PlainTime(0, 2));
+TemporalHelpers.assertDuration(result5, 0, 0, 0, 0, 0, 59, 1, 1, 1, 1, "minutes balance");
+
+// This one is different because hours are later balanced again in BalanceDuration
+const result6 = time.since(new Temporal.PlainTime(2));
+TemporalHelpers.assertDuration(result6, 0, 0, 0, 0, 0, -58, -58, -998, -998, -999, "hours balance");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/basic.js
new file mode 100644
index 0000000000..2c4a6da6ae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/basic.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Basic usage
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const one = new Temporal.PlainTime(15, 23, 30, 123, 456, 789);
+const two = new Temporal.PlainTime(14, 23, 30, 123, 456, 789);
+const three = new Temporal.PlainTime(13, 30, 30, 123, 456, 789);
+
+TemporalHelpers.assertDuration(one.since(two),
+ 0, 0, 0, 0, /* hours = */ 1, 0, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(two.since(one),
+ 0, 0, 0, 0, /* hours = */ -1, 0, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(one.since(three),
+ 0, 0, 0, 0, /* hours = */ 1, 53, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(three.since(one),
+ 0, 0, 0, 0, /* hours = */ -1, -53, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/branding.js
new file mode 100644
index 0000000000..58e28d48d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const since = Temporal.PlainTime.prototype.since;
+
+assert.sameValue(typeof since, "function");
+
+const args = [new Temporal.PlainTime(12)];
+
+assert.throws(TypeError, () => since.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => since.apply(null, args), "null");
+assert.throws(TypeError, () => since.apply(true, args), "true");
+assert.throws(TypeError, () => since.apply("", args), "empty string");
+assert.throws(TypeError, () => since.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => since.apply(1, args), "1");
+assert.throws(TypeError, () => since.apply({}, args), "plain object");
+assert.throws(TypeError, () => since.apply(Temporal.PlainTime, args), "Temporal.PlainTime");
+assert.throws(TypeError, () => since.apply(Temporal.PlainTime.prototype, args), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/builtin.js
new file mode 100644
index 0000000000..b1d7800d31
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: >
+ Tests that Temporal.PlainTime.prototype.since
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainTime.prototype.since),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.prototype.since),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.prototype.since),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.prototype.since.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-invalid-string.js
new file mode 100644
index 0000000000..730fe43204
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-invalid-string.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(13, 35, 57, 987, 654, 321);
+const badValues = [
+ "era",
+ "eraYear",
+ "year",
+ "month",
+ "week",
+ "day",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string"
+];
+for (const largestUnit of badValues) {
+ assert.throws(RangeError, () => later.since(earlier, { largestUnit }),
+ `"${largestUnit}" is not a valid value for largestUnit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-plurals-accepted.js
new file mode 100644
index 0000000000..b216349ac0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-plurals-accepted.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainTime(13, 35, 57, 988, 655, 322);
+const validUnits = [
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => later.since(earlier, { largestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-smallestunit-mismatch.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-smallestunit-mismatch.js
new file mode 100644
index 0000000000..340d757b34
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-smallestunit-mismatch.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: RangeError thrown when smallestUnit is larger than largestUnit
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(13, 35, 57, 987, 654, 321);
+const units = ["hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+for (let largestIdx = 1; largestIdx < units.length; largestIdx++) {
+ for (let smallestIdx = 0; smallestIdx < largestIdx; smallestIdx++) {
+ const largestUnit = units[largestIdx];
+ const smallestUnit = units[smallestIdx];
+ assert.throws(RangeError, () => later.since(earlier, { largestUnit, smallestUnit }));
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-undefined.js
new file mode 100644
index 0000000000..43b1b4a58b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-undefined.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Fallback value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(13, 35, 57, 987, 654, 321);
+
+const explicit = later.since(earlier, { largestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 1, 1, 1, 987, 654, 321, "default largestUnit is hour");
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 1, 1, 1, 987, 654, 321, "default largestUnit is hour");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-wrong-type.js
new file mode 100644
index 0000000000..f9ecd7f958
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-wrong-type.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(13, 35, 57, 987, 654, 321);
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "second",
+ (largestUnit) => later.since(earlier, { largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 3661, 987, 654, 321, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit.js
new file mode 100644
index 0000000000..61a7c65efe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: PlainTime.since with various largestUnit values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+const fourFortyEight = new Temporal.PlainTime(4, 48, 55);
+const elevenFiftyNine = new Temporal.PlainTime(11, 59, 58);
+TemporalHelpers.assertDuration(elevenFiftyNine.since(fourFortyEight), 0, 0, 0, 0, 7, 11, 3, 0, 0, 0, 'does not include higher units than necessary (largest unit unspecified)');
+TemporalHelpers.assertDuration(elevenFiftyNine.since(fourFortyEight, { largestUnit: 'auto' }), 0, 0, 0, 0, 7, 11, 3, 0, 0, 0, 'does not include higher units than necessary (largest unit is auto)');
+TemporalHelpers.assertDuration(elevenFiftyNine.since(fourFortyEight, { largestUnit: 'hours' }), 0, 0, 0, 0, 7, 11, 3, 0, 0, 0, 'does not include higher units than necessary (largest unit is hours)');
+TemporalHelpers.assertDuration(elevenFiftyNine.since(fourFortyEight, { largestUnit: 'minutes' }), 0, 0, 0, 0, 0, 431, 3, 0, 0, 0, 'does not include higher units than necessary (largest unit is minutes)');
+TemporalHelpers.assertDuration(elevenFiftyNine.since(fourFortyEight, { largestUnit: 'seconds' }), 0, 0, 0, 0, 0, 0, 25863, 0, 0, 0, 'does not include higher units than necessary (largest unit is seconds)');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/leap-second.js
new file mode 100644
index 0000000000..428352a5f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/leap-second.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Leap second is a valid ISO string for PlainTime
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(23, 59, 59);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.since(arg);
+TemporalHelpers.assertDuration(
+ result1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for PlainTime"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.since(arg);
+TemporalHelpers.assertDuration(
+ result2,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "second: 60 is ignored in property bag for PlainTime"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/length.js
new file mode 100644
index 0000000000..1905d55029
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Temporal.PlainTime.prototype.since.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.since, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/name.js
new file mode 100644
index 0000000000..5ba97cebbb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Temporal.PlainTime.prototype.since.name is "since".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.since, "name", {
+ value: "since",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/not-a-constructor.js
new file mode 100644
index 0000000000..43b9cd80f9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: >
+ Temporal.PlainTime.prototype.since does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainTime.prototype.since();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.prototype.since), false,
+ "isConstructor(Temporal.PlainTime.prototype.since)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-invalid.js
new file mode 100644
index 0000000000..da9c831983
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-invalid.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: TypeError thrown when a primitive is passed as the options argument
+features: [Temporal]
+---*/
+
+const values = [null, true, "hello", Symbol("foo"), 1, 1n];
+const time = new Temporal.PlainTime(15, 23, 30, 123, 456, 789);
+const one = new Temporal.PlainTime(16, 23, 30, 123, 456, 789);
+
+for (const badOptions of values) {
+ assert.throws(TypeError, () => time.since(one, badOptions));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-object.js
new file mode 100644
index 0000000000..1212676bd4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-object.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+
+const result1 = instance.since(new Temporal.PlainTime(12, 34, 56), {});
+TemporalHelpers.assertDuration(
+ result1, 0, 0, 0, 0, -12, -34, -56, 0, 0, 0,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.since(new Temporal.PlainTime(12, 34, 56), () => {});
+TemporalHelpers.assertDuration(
+ result2, 0, 0, 0, 0, -12, -34, -56, 0, 0, 0,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-undefined.js
new file mode 100644
index 0000000000..e31734100d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-undefined.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainTime(18, 34, 56, 987, 654, 322);
+
+const explicit = later.since(earlier, undefined);
+assert.sameValue(explicit.hours, 6, "default largest unit is hours");
+assert.sameValue(explicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding");
+
+const implicit = later.since(earlier);
+assert.sameValue(implicit.hours, 6, "default largest unit is hours");
+assert.sameValue(implicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-wrong-type.js
new file mode 100644
index 0000000000..78e17fa2f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-wrong-type.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainTime();
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.since(new Temporal.PlainTime(12, 34, 56), value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/order-of-operations.js
new file mode 100644
index 0000000000..33f3b9bac5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/order-of-operations.js
@@ -0,0 +1,95 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Properties on an object passed to since() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalTime
+ "get other.hour",
+ "get other.hour.valueOf",
+ "call other.hour.valueOf",
+ "get other.microsecond",
+ "get other.microsecond.valueOf",
+ "call other.microsecond.valueOf",
+ "get other.millisecond",
+ "get other.millisecond.valueOf",
+ "call other.millisecond.valueOf",
+ "get other.minute",
+ "get other.minute.valueOf",
+ "call other.minute.valueOf",
+ "get other.nanosecond",
+ "get other.nanosecond.valueOf",
+ "call other.nanosecond.valueOf",
+ "get other.second",
+ "get other.second.valueOf",
+ "call other.second.valueOf",
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.roundingIncrement",
+ "get options.roundingIncrement",
+ "getOwnPropertyDescriptor options.roundingMode",
+ "get options.roundingMode",
+ "getOwnPropertyDescriptor options.largestUnit",
+ "get options.largestUnit",
+ "getOwnPropertyDescriptor options.smallestUnit",
+ "get options.smallestUnit",
+ "getOwnPropertyDescriptor options.additional",
+ "get options.additional",
+ // GetDifferenceSettings
+ "get options.largestUnit.toString",
+ "call options.largestUnit.toString",
+ "get options.roundingIncrement.valueOf",
+ "call options.roundingIncrement.valueOf",
+ "get options.roundingMode.toString",
+ "call options.roundingMode.toString",
+ "get options.smallestUnit.toString",
+ "call options.smallestUnit.toString",
+];
+const actual = [];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const other = TemporalHelpers.propertyBagObserver(actual, {
+ hour: 1.7,
+ minute: 1.7,
+ second: 1.7,
+ millisecond: 1.7,
+ microsecond: 1.7,
+ nanosecond: 1.7,
+ calendar: "iso8601",
+}, "other");
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ roundingIncrement: 1,
+ roundingMode: "trunc",
+ largestUnit: "hours",
+ smallestUnit: "nanoseconds",
+ additional: true,
+}, "options");
+
+const result = instance.since(other, options);
+assert.compareArray(actual, expected, "order of operations");
+
+actual.splice(0); // clear
+
+// short-circuit does not skip reading options
+const identicalPropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ hour: 12,
+ minute: 34,
+ second: 56,
+ millisecond: 987,
+ microsecond: 654,
+ nanosecond: 321,
+}, "other");
+instance.since(identicalPropertyBag, options);
+assert.compareArray(actual, expected, "order of operations with identical times");
+
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/plaintime-propertybag-no-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/plaintime-propertybag-no-time-units.js
new file mode 100644
index 0000000000..563d9cd8b5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/plaintime-propertybag-no-time-units.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Missing time units in property bag default to 0
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(1, 0, 0, 0, 0, 1);
+
+const props = {};
+assert.throws(TypeError, () => instance.since(props), "TypeError if no properties are present");
+
+props.minute = 30;
+const result = instance.since(props);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 30, 0, 0, 0, 1, "missing time units default to 0");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/prop-desc.js
new file mode 100644
index 0000000000..1487fef147
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: The "since" property of Temporal.PlainTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.prototype.since,
+ "function",
+ "`typeof PlainTime.prototype.since` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime.prototype, "since", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/result-sub-second.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/result-sub-second.js
new file mode 100644
index 0000000000..8821442df1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/result-sub-second.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Supports sub-second precision
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const time1 = Temporal.PlainTime.from("10:23:15");
+const time2 = Temporal.PlainTime.from("17:15:57.250250250");
+
+TemporalHelpers.assertDuration(time2.since(time1, { largestUnit: "milliseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, /* milliseconds = */ 24762250, 250, 250, "milliseconds");
+
+TemporalHelpers.assertDuration(time2.since(time1, { largestUnit: "microseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, /* milliseconds = */ 0, 24762250250, 250, "microseconds");
+
+TemporalHelpers.assertDuration(time2.since(time1, { largestUnit: "nanoseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, /* milliseconds = */ 0, 0, 24762250250250, "nanoseconds");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/round-cross-unit-boundary.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/round-cross-unit-boundary.js
new file mode 100644
index 0000000000..7d6be19a63
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/round-cross-unit-boundary.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Rounding can cross unit boundaries up to largestUnit
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime();
+const later = new Temporal.PlainTime(1, 59, 59);
+const duration = earlier.since(later, { largestUnit: "hours", smallestUnit: "minutes", roundingMode: "expand" });
+TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, "-1:60 balances to -2 hours");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-hours.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-hours.js
new file mode 100644
index 0000000000..1318ce0a71
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-hours.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(3, 12, 34, 123, 456, 789);
+const later = new Temporal.PlainTime(13, 47, 57, 988, 655, 322);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "hours", roundingIncrement: 1 }),
+ 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "hours", roundingIncrement: 2 }),
+ 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "hours", roundingIncrement: 3 }),
+ 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "hours", roundingIncrement: 4 }),
+ 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "hours", roundingIncrement: 6 }),
+ 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "hours", roundingIncrement: 8 }),
+ 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "hours", roundingIncrement: 12 }),
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "hours");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-invalid.js
new file mode 100644
index 0000000000..e1a03301e1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-invalid.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Tests roundingIncrement restrictions.
+features: [Temporal]
+---*/
+
+const earlier = Temporal.PlainTime.from("08:22:36.123456789");
+const later = Temporal.PlainTime.from("12:39:40.987654321");
+
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "hours", roundingIncrement: 11 }));
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "minutes", roundingIncrement: 29 }));
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "seconds", roundingIncrement: 29 }));
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 29 }));
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 29 }));
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 29 }));
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "hours", roundingIncrement: 24 }));
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "minutes", roundingIncrement: 60 }));
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "seconds", roundingIncrement: 60 }));
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 1000 }));
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 1000 }));
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 1000 }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-microseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-microseconds.js
new file mode 100644
index 0000000000..1013fb750e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-microseconds.js
@@ -0,0 +1,61 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(3, 12, 34, 123, 456, 789);
+const later = new Temporal.PlainTime(13, 47, 57, 988, 655, 322);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 1 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 2 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 4 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 196, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 5 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 195, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 8 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 192, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 10 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 190, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 20 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 180, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 25 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 175, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 40 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 160, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 50 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 150, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 100 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 100, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 125 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 125, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 200 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 0, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 250 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 0, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 500 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 0, 0, "microseconds");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-milliseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-milliseconds.js
new file mode 100644
index 0000000000..5a74a65fea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-milliseconds.js
@@ -0,0 +1,61 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(3, 12, 34, 123, 456, 789);
+const later = new Temporal.PlainTime(13, 47, 57, 988, 655, 322);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 1 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 2 }),
+ 0, 0, 0, 0, 10, 35, 23, 864, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 4 }),
+ 0, 0, 0, 0, 10, 35, 23, 864, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 5 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 8 }),
+ 0, 0, 0, 0, 10, 35, 23, 864, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 10 }),
+ 0, 0, 0, 0, 10, 35, 23, 860, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 20 }),
+ 0, 0, 0, 0, 10, 35, 23, 860, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 25 }),
+ 0, 0, 0, 0, 10, 35, 23, 850, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 40 }),
+ 0, 0, 0, 0, 10, 35, 23, 840, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 50 }),
+ 0, 0, 0, 0, 10, 35, 23, 850, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 100 }),
+ 0, 0, 0, 0, 10, 35, 23, 800, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 125 }),
+ 0, 0, 0, 0, 10, 35, 23, 750, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 200 }),
+ 0, 0, 0, 0, 10, 35, 23, 800, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 250 }),
+ 0, 0, 0, 0, 10, 35, 23, 750, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 500 }),
+ 0, 0, 0, 0, 10, 35, 23, 500, 0, 0, "milliseconds");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-minutes.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-minutes.js
new file mode 100644
index 0000000000..e765503076
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-minutes.js
@@ -0,0 +1,49 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(3, 12, 34, 123, 456, 789);
+const later = new Temporal.PlainTime(13, 47, 57, 988, 655, 322);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes", roundingIncrement: 1 }),
+ 0, 0, 0, 0, 10, 35, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes", roundingIncrement: 2 }),
+ 0, 0, 0, 0, 10, 34, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes", roundingIncrement: 3 }),
+ 0, 0, 0, 0, 10, 33, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes", roundingIncrement: 4 }),
+ 0, 0, 0, 0, 10, 32, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes", roundingIncrement: 5 }),
+ 0, 0, 0, 0, 10, 35, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes", roundingIncrement: 6 }),
+ 0, 0, 0, 0, 10, 30, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes", roundingIncrement: 10 }),
+ 0, 0, 0, 0, 10, 30, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes", roundingIncrement: 12 }),
+ 0, 0, 0, 0, 10, 24, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes", roundingIncrement: 15 }),
+ 0, 0, 0, 0, 10, 30, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes", roundingIncrement: 20 }),
+ 0, 0, 0, 0, 10, 20, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes", roundingIncrement: 30 }),
+ 0, 0, 0, 0, 10, 30, 0, 0, 0, 0, "minutes");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-nan.js
new file mode 100644
index 0000000000..62bfdb33d2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-nan.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: RangeError thrown when roundingIncrement option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaintime.prototype.since step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainTime(13, 35, 57, 988, 655, 322);
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-nanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-nanoseconds.js
new file mode 100644
index 0000000000..aa17ab168c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-nanoseconds.js
@@ -0,0 +1,61 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(3, 12, 34, 123, 456, 789);
+const later = new Temporal.PlainTime(13, 47, 57, 988, 655, 322);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 1 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 533, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 2 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 532, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 4 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 532, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 5 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 530, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 8 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 528, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 10 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 530, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 20 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 520, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 25 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 525, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 40 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 520, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 50 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 500, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 100 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 500, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 125 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 500, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 200 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 400, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 250 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 500, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 500 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 500, "nanoseconds");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-non-integer.js
new file mode 100644
index 0000000000..f728a4c75f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-non-integer.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Rounding for roundingIncrement option
+info: |
+ ToTemporalRoundingIncrement ( _normalizedOptions_ )
+
+ 1. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, *"number"*, *undefined*, *1*<sub>𝔽</sub>).
+ 2. If _increment_ is not finite, throw a *RangeError* exception.
+ 3. Let _integerIncrement_ be truncate(ℝ(_increment_)).
+ 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception.
+ 5. Return _integerIncrement_.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(12, 34, 56, 0, 0, 5);
+const result = later.since(earlier, { roundingIncrement: 2.5, roundingMode: "trunc" });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, "roundingIncrement 2.5 truncates to 2");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-out-of-range.js
new file mode 100644
index 0000000000..74ad0e56dd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-out-of-range.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: RangeError thrown when roundingIncrement option out of range
+info: |
+ ToTemporalRoundingIncrement ( _normalizedOptions_ )
+
+ 1. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, *"number"*, *undefined*, *1*<sub>𝔽</sub>).
+ 2. If _increment_ is not finite, throw a *RangeError* exception.
+ 3. Let _integerIncrement_ be truncate(ℝ(_increment_)).
+ 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception.
+ 5. Return _integerIncrement_.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(12, 34, 56, 0, 0, 5);
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: -1 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: 0 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: 0.9 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: 1e9 + 1 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: Infinity }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-seconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-seconds.js
new file mode 100644
index 0000000000..48ae41c282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-seconds.js
@@ -0,0 +1,49 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(3, 12, 34, 123, 456, 789);
+const later = new Temporal.PlainTime(13, 47, 57, 988, 655, 322);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds", roundingIncrement: 1 }),
+ 0, 0, 0, 0, 10, 35, 23, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds", roundingIncrement: 2 }),
+ 0, 0, 0, 0, 10, 35, 22, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds", roundingIncrement: 3 }),
+ 0, 0, 0, 0, 10, 35, 21, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds", roundingIncrement: 4 }),
+ 0, 0, 0, 0, 10, 35, 20, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds", roundingIncrement: 5 }),
+ 0, 0, 0, 0, 10, 35, 20, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds", roundingIncrement: 6 }),
+ 0, 0, 0, 0, 10, 35, 18, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds", roundingIncrement: 10 }),
+ 0, 0, 0, 0, 10, 35, 20, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds", roundingIncrement: 12 }),
+ 0, 0, 0, 0, 10, 35, 12, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds", roundingIncrement: 15 }),
+ 0, 0, 0, 0, 10, 35, 15, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds", roundingIncrement: 20 }),
+ 0, 0, 0, 0, 10, 35, 20, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds", roundingIncrement: 30 }),
+ 0, 0, 0, 0, 10, 35, 0, 0, 0, 0, "seconds");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-undefined.js
new file mode 100644
index 0000000000..816763660e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-undefined.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Fallback value for roundingIncrement option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaintime.prototype.since step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainTime(13, 35, 57, 988, 655, 322);
+
+const explicit = later.since(earlier, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, "default roundingIncrement is 1");
+
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, "default roundingIncrement is 1");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-wrong-type.js
new file mode 100644
index 0000000000..a8d580d49a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-wrong-type.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Type conversions for roundingIncrement option
+info: |
+ sec-getoption step 8.a:
+ a. Set _value_ to ? ToNumber(value).
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaintime.prototype.since step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainTime(13, 35, 57, 988, 655, 322);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => later.since(earlier, { roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-ceil.js
new file mode 100644
index 0000000000..2c59d6364e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-ceil.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Tests calculations with roundingMode "ceil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainTime(12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 5], [0, 0, 0, 0, -4]],
+ ["minutes", [0, 0, 0, 0, 4, 18], [0, 0, 0, 0, -4, -17]],
+ ["seconds", [0, 0, 0, 0, 4, 17, 5], [0, 0, 0, 0, -4, -17, -4]],
+ ["milliseconds", [0, 0, 0, 0, 4, 17, 4, 865], [0, 0, 0, 0, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 4, 17, 4, 864, 198], [0, 0, 0, 0, -4, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197, 500], [0, 0, 0, 0, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "ceil";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-expand.js
new file mode 100644
index 0000000000..fc0ffaf8e0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-expand.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Tests calculations with roundingMode "expand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainTime(12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 5], [0, 0, 0, 0, -5]],
+ ["minutes", [0, 0, 0, 0, 4, 18], [0, 0, 0, 0, -4, -18]],
+ ["seconds", [0, 0, 0, 0, 4, 17, 5], [0, 0, 0, 0, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 4, 17, 4, 865], [0, 0, 0, 0, -4, -17, -4, -865]],
+ ["microseconds", [0, 0, 0, 0, 4, 17, 4, 864, 198], [0, 0, 0, 0, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197, 500], [0, 0, 0, 0, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "expand";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-floor.js
new file mode 100644
index 0000000000..2063878a22
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-floor.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Tests calculations with roundingMode "floor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainTime(12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 4], [0, 0, 0, 0, -5]],
+ ["minutes", [0, 0, 0, 0, 4, 17], [0, 0, 0, 0, -4, -18]],
+ ["seconds", [0, 0, 0, 0, 4, 17, 4], [0, 0, 0, 0, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 4, 17, 4, 864], [0, 0, 0, 0, -4, -17, -4, -865]],
+ ["microseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197], [0, 0, 0, 0, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197, 500], [0, 0, 0, 0, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "floor";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..319a618aed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfCeil.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Tests calculations with roundingMode "halfCeil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainTime(12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 4], [0, 0, 0, 0, -4]],
+ ["minutes", [0, 0, 0, 0, 4, 17], [0, 0, 0, 0, -4, -17]],
+ ["seconds", [0, 0, 0, 0, 4, 17, 5], [0, 0, 0, 0, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 4, 17, 4, 864], [0, 0, 0, 0, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 4, 17, 4, 864, 198], [0, 0, 0, 0, -4, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197, 500], [0, 0, 0, 0, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "halfCeil";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfEven.js
new file mode 100644
index 0000000000..b6b3edbf50
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfEven.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Tests calculations with roundingMode "halfEven".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainTime(12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 4], [0, 0, 0, 0, -4]],
+ ["minutes", [0, 0, 0, 0, 4, 17], [0, 0, 0, 0, -4, -17]],
+ ["seconds", [0, 0, 0, 0, 4, 17, 5], [0, 0, 0, 0, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 4, 17, 4, 864], [0, 0, 0, 0, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 4, 17, 4, 864, 198], [0, 0, 0, 0, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197, 500], [0, 0, 0, 0, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "halfEven";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..a44ce32e5c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfExpand.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Tests calculations with roundingMode "halfExpand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainTime(12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 4], [0, 0, 0, 0, -4]],
+ ["minutes", [0, 0, 0, 0, 4, 17], [0, 0, 0, 0, -4, -17]],
+ ["seconds", [0, 0, 0, 0, 4, 17, 5], [0, 0, 0, 0, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 4, 17, 4, 864], [0, 0, 0, 0, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 4, 17, 4, 864, 198], [0, 0, 0, 0, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197, 500], [0, 0, 0, 0, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "halfExpand";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..560047cf1d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfFloor.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Tests calculations with roundingMode "halfFloor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainTime(12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 4], [0, 0, 0, 0, -4]],
+ ["minutes", [0, 0, 0, 0, 4, 17], [0, 0, 0, 0, -4, -17]],
+ ["seconds", [0, 0, 0, 0, 4, 17, 5], [0, 0, 0, 0, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 4, 17, 4, 864], [0, 0, 0, 0, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197], [0, 0, 0, 0, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197, 500], [0, 0, 0, 0, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "halfFloor";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..9658dfc732
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfTrunc.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Tests calculations with roundingMode "halfTrunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainTime(12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 4], [0, 0, 0, 0, -4]],
+ ["minutes", [0, 0, 0, 0, 4, 17], [0, 0, 0, 0, -4, -17]],
+ ["seconds", [0, 0, 0, 0, 4, 17, 5], [0, 0, 0, 0, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 4, 17, 4, 864], [0, 0, 0, 0, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197], [0, 0, 0, 0, -4, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197, 500], [0, 0, 0, 0, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "halfTrunc";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..ba57777e2c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-invalid-string.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(13, 35, 57, 123, 987, 500);
+for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) {
+ assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "microsecond", roundingMode }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-trunc.js
new file mode 100644
index 0000000000..e1b447c453
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-trunc.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Tests calculations with roundingMode "trunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainTime(12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 4], [0, 0, 0, 0, -4]],
+ ["minutes", [0, 0, 0, 0, 4, 17], [0, 0, 0, 0, -4, -17]],
+ ["seconds", [0, 0, 0, 0, 4, 17, 4], [0, 0, 0, 0, -4, -17, -4]],
+ ["milliseconds", [0, 0, 0, 0, 4, 17, 4, 864], [0, 0, 0, 0, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197], [0, 0, 0, 0, -4, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197, 500], [0, 0, 0, 0, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "trunc";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-undefined.js
new file mode 100644
index 0000000000..f437428f15
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-undefined.js
@@ -0,0 +1,93 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = Temporal.PlainTime.from("08:22:36.123456789");
+const later = Temporal.PlainTime.from("12:39:40.987654321");
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "hours", roundingMode: undefined }),
+ 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "hours" }),
+ 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit: "hours", roundingMode: undefined }),
+ 0, 0, 0, 0, -4, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit: "hours" }),
+ 0, 0, 0, 0, -4, 0, 0, 0, 0, 0, "hours");
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes", roundingMode: undefined }),
+ 0, 0, 0, 0, 4, 17, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes" }),
+ 0, 0, 0, 0, 4, 17, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit: "minutes", roundingMode: undefined }),
+ 0, 0, 0, 0, -4, -17, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit: "minutes" }),
+ 0, 0, 0, 0, -4, -17, 0, 0, 0, 0, "minutes");
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds", roundingMode: undefined }),
+ 0, 0, 0, 0, 4, 17, 4, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds" }),
+ 0, 0, 0, 0, 4, 17, 4, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit: "seconds", roundingMode: undefined }),
+ 0, 0, 0, 0, -4, -17, -4, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit: "seconds" }),
+ 0, 0, 0, 0, -4, -17, -4, 0, 0, 0, "seconds");
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingMode: undefined }),
+ 0, 0, 0, 0, 4, 17, 4, 864, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds" }),
+ 0, 0, 0, 0, 4, 17, 4, 864, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit: "milliseconds", roundingMode: undefined }),
+ 0, 0, 0, 0, -4, -17, -4, -864, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit: "milliseconds" }),
+ 0, 0, 0, 0, -4, -17, -4, -864, 0, 0, "milliseconds");
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingMode: undefined }),
+ 0, 0, 0, 0, 4, 17, 4, 864, 197, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds" }),
+ 0, 0, 0, 0, 4, 17, 4, 864, 197, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit: "microseconds", roundingMode: undefined }),
+ 0, 0, 0, 0, -4, -17, -4, -864, -197, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit: "microseconds" }),
+ 0, 0, 0, 0, -4, -17, -4, -864, -197, 0, "microseconds");
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingMode: undefined }),
+ 0, 0, 0, 0, 4, 17, 4, 864, 197, 532, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds" }),
+ 0, 0, 0, 0, 4, 17, 4, 864, 197, 532, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit: "nanoseconds", roundingMode: undefined }),
+ 0, 0, 0, 0, -4, -17, -4, -864, -197, -532, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit: "nanoseconds" }),
+ 0, 0, 0, 0, -4, -17, -4, -864, -197, -532, "nanoseconds");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..3ccf979f0d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-wrong-type.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(13, 35, 57, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => later.since(earlier, { smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 1, 1, 1, 123, 987, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..28dd00c591
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-invalid-string.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(13, 35, 57, 987, 654, 321);
+const badValues = [
+ "era",
+ "eraYear",
+ "year",
+ "month",
+ "week",
+ "day",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => later.since(earlier, { smallestUnit }),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..c6d998214b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-plurals-accepted.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainTime(13, 35, 57, 988, 655, 322);
+const validUnits = [
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => later.since(earlier, { smallestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-undefined.js
new file mode 100644
index 0000000000..ba00b5348c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-undefined.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(13, 35, 57, 987, 654, 321);
+
+const explicit = later.since(earlier, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 1, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 1, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
+const lambda = later.since(earlier, () => {});
+TemporalHelpers.assertDuration(lambda, 0, 0, 0, 0, 1, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..915b81a772
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-wrong-type.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(13, 35, 57, 987, 654, 321);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => later.since(earlier, { smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 1, 1, 1, 987, 654, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/year-zero.js
new file mode 100644
index 0000000000..86f60c8ed7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/year-zero.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-12-07T03:24:30",
+ "-000000-12-07T03:24:30+01:00",
+ "-000000-12-07T03:24:30+00:00[UTC]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-max.js
new file mode 100644
index 0000000000..6e24c81554
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-max.js
@@ -0,0 +1,58 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: Maximum allowed duration
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+
+const maxCases = [
+ ["P4294967295Y104249991374DT7H36M31.999999999S", "string with max years"],
+ [{ years: 4294967295, days: 104249991374, nanoseconds: 27391999999999 }, "property bag with max years"],
+ ["P4294967295M104249991374DT7H36M31.999999999S", "string with max weeks"],
+ [{ months: 4294967295, days: 104249991374, nanoseconds: 27391999999999 }, "property bag with max months"],
+ ["P4294967295W104249991374DT7H36M31.999999999S", "string with max weeks"],
+ [{ weeks: 4294967295, days: 104249991374, nanoseconds: 27391999999999 }, "property bag with max weeks"],
+ ["P104249991374DT7H36M31.999999999S", "string with max days"],
+ [{ days: 104249991374, nanoseconds: 27391999999999 }, "property bag with max days"],
+ ["PT2501999792983H36M31.999999999S", "string with max hours"],
+ [{ hours: 2501999792983, nanoseconds: 2191999999999 }, "property bag with max hours"],
+ ["PT150119987579016M31.999999999S", "string with max minutes"],
+ [{ minutes: 150119987579016, nanoseconds: 31999999999 }, "property bag with max minutes"],
+ ["PT9007199254740991.999999999S", "string with max seconds"],
+ [{ seconds: 9007199254740991, nanoseconds: 999999999 }, "property bag with max seconds"],
+];
+
+for (const [arg, descr] of maxCases) {
+ const result = instance.subtract(arg);
+ TemporalHelpers.assertPlainTime(result, 16, 23, 28, 0, 0, 1, `operation succeeds with ${descr}`);
+}
+
+const minCases = [
+ ["-P4294967295Y104249991374DT7H36M31.999999999S", "string with min years"],
+ [{ years: -4294967295, days: -104249991374, nanoseconds: -27391999999999 }, "property bag with min years"],
+ ["-P4294967295M104249991374DT7H36M31.999999999S", "string with min months"],
+ [{ months: -4294967295, days: -104249991374, nanoseconds: -27391999999999 }, "property bag with min months"],
+ ["-P4294967295W104249991374DT7H36M31.999999999S", "string with min weeks"],
+ [{ weeks: -4294967295, days: -104249991374, nanoseconds: -27391999999999 }, "property bag with min weeks"],
+ ["-P104249991374DT7H36M31.999999999S", "string with min days"],
+ [{ days: -104249991374, nanoseconds: -27391999999999 }, "property bag with min days"],
+ ["-PT2501999792983H36M31.999999999S", "string with min hours"],
+ [{ hours: -2501999792983, nanoseconds: -2191999999999 }, "property bag with min hours"],
+ ["-PT150119987579016M31.999999999S", "string with min minutes"],
+ [{ minutes: -150119987579016, nanoseconds: -31999999999 }, "property bag with min minutes"],
+ ["-PT9007199254740991.999999999S", "string with min seconds"],
+ [{ seconds: -9007199254740991, nanoseconds: -999999999 }, "property bag with min seconds"],
+];
+
+for (const [arg, descr] of minCases) {
+ const result = instance.subtract(arg);
+ TemporalHelpers.assertPlainTime(result, 7, 36, 31, 999, 999, 999, `operation succeeds with ${descr}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-out-of-range.js
new file mode 100644
index 0000000000..8227cc217b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-out-of-range.js
@@ -0,0 +1,75 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: Duration-like argument that is out of range
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+
+const cases = [
+ // 2^32 = 4294967296
+ ["P4294967296Y", "string with years > max"],
+ [{ years: 4294967296 }, "property bag with years > max"],
+ ["-P4294967296Y", "string with years < min"],
+ [{ years: -4294967296 }, "property bag with years < min"],
+ ["P4294967296M", "string with months > max"],
+ [{ months: 4294967296 }, "property bag with months > max"],
+ ["-P4294967296M", "string with months < min"],
+ [{ months: -4294967296 }, "property bag with months < min"],
+ ["P4294967296W", "string with weeks > max"],
+ [{ weeks: 4294967296 }, "property bag with weeks > max"],
+ ["-P4294967296W", "string with weeks < min"],
+ [{ weeks: -4294967296 }, "property bag with weeks < min"],
+
+ // ceil(max safe integer / 86400) = 104249991375
+ ["P104249991375D", "string with days > max"],
+ [{ days: 104249991375 }, "property bag with days > max"],
+ ["P104249991374DT24H", "string where hours balance into days > max"],
+ [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"],
+ ["-P104249991375D", "string with days < min"],
+ [{ days: -104249991375 }, "property bag with days < min"],
+ ["-P104249991374DT24H", "string where hours balance into days < min"],
+ [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"],
+
+ // ceil(max safe integer / 3600) = 2501999792984
+ ["PT2501999792984H", "string with hours > max"],
+ [{ hours: 2501999792984 }, "property bag with hours > max"],
+ ["PT2501999792983H60M", "string where minutes balance into hours > max"],
+ [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"],
+ ["-PT2501999792984H", "string with hours < min"],
+ [{ hours: -2501999792984 }, "property bag with hours < min"],
+ ["-PT2501999792983H60M", "string where minutes balance into hours < min"],
+ [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"],
+
+ // ceil(max safe integer / 60) = 150119987579017
+ ["PT150119987579017M", "string with minutes > max"],
+ [{ minutes: 150119987579017 }, "property bag with minutes > max"],
+ ["PT150119987579016M60S", "string where seconds balance into minutes > max"],
+ [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"],
+ ["-PT150119987579017M", "string with minutes < min"],
+ [{ minutes: -150119987579017 }, "property bag with minutes < min"],
+ ["-PT150119987579016M60S", "string where seconds balance into minutes < min"],
+ [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"],
+
+ // 2^53 = 9007199254740992
+ ["PT9007199254740992S", "string with seconds > max"],
+ [{ seconds: 9007199254740992 }, "property bag with seconds > max"],
+ [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"],
+ ["-PT9007199254740992S", "string with seconds < min"],
+ [{ seconds: -9007199254740992 }, "property bag with seconds < min"],
+ [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"],
+];
+
+for (const [arg, descr] of cases) {
+ assert.throws(RangeError, () => instance.subtract(arg), `${descr} is out of range`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-precision-exact-numerical-values.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-precision-exact-numerical-values.js
new file mode 100644
index 0000000000..a595fa204f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-precision-exact-numerical-values.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: >
+ Duration-like argument performs the range check with minimal floating point
+ precision loss
+features: [Temporal]
+---*/
+
+// Based on a test case by André Bargull
+
+const instance = new Temporal.PlainTime();
+
+const cases = [
+ [
+ {
+ milliseconds: 4503599627370497_000, // ℝ(𝔽(4503599627370497000)) = 4503599627370497024
+ microseconds: 4503599627370495_000000, // ℝ(𝔽(4503599627370495000000)) = 4503599627370494951424
+ },
+ // 4503599627370497024 / 1000 + 4503599627370494951424 / 1000000 is
+ // 9007199254740991.975424, which is below the limit of 2**53
+ "case where floating point inaccuracy brings total below limit, positive"
+ ],
+ [
+ {
+ milliseconds: -4503599627370497_000,
+ microseconds: -4503599627370495_000000,
+ },
+ "case where floating point inaccuracy brings total below limit, negative"
+ ],
+];
+
+for (const [arg, descr] of cases) {
+ instance.subtract(arg); // should not throw
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration.js
new file mode 100644
index 0000000000..99c1990791
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: Duration arguments are supported.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(15, 23, 30, 123, 456, 789);
+const duration = Temporal.Duration.from("PT16H");
+TemporalHelpers.assertPlainTime(plainTime.subtract(duration),
+ 23, 23, 30, 123, 456, 789);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-higher-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-higher-units.js
new file mode 100644
index 0000000000..43588561a9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-higher-units.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: Higher units are ignored.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(15, 23, 30, 123, 456, 789);
+const values = [
+ new Temporal.Duration(0, 0, 0, 1),
+ new Temporal.Duration(0, 0, 1),
+ new Temporal.Duration(0, 1),
+ new Temporal.Duration(1),
+ { days: 1 },
+ { weeks: 1 },
+ { months: 1 },
+ { years: 1 },
+ "P1D",
+ "P1W",
+ "P1M",
+ "P1Y",
+];
+for (const value of values) {
+ TemporalHelpers.assertPlainTime(plainTime.subtract(value),
+ 15, 23, 30, 123, 456, 789);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-invalid-property.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-invalid-property.js
new file mode 100644
index 0000000000..6fab0a1c32
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-invalid-property.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: temporalDurationLike object must contain at least one correctly spelled property
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(15, 30, 45, 987, 654, 321);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({}),
+ "Throws TypeError if no property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({ nonsense: true }),
+ "Throws TypeError if no recognized property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({ sign: 1 }),
+ "Sign property is not recognized"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-mixed-sign.js
new file mode 100644
index 0000000000..234ad0fedb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-mixed-sign.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: Positive and negative values in the temporalDurationLike argument are not acceptable
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(15, 30, 45, 987, 654, 321);
+
+assert.throws(
+ RangeError,
+ () => instance.subtract({ hours: 1, minutes: -30 }),
+ `mixed positive and negative values always throw`
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-not-object.js
new file mode 100644
index 0000000000..c77ced8f6b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-not-object.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: Passing a primitive other than string to subtract() throws
+features: [Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(15, 30, 45, 987, 654, 321);
+assert.throws(TypeError, () => instance.subtract(undefined), "undefined");
+assert.throws(TypeError, () => instance.subtract(null), "null");
+assert.throws(TypeError, () => instance.subtract(true), "boolean");
+assert.throws(RangeError, () => instance.subtract(""), "empty string");
+assert.throws(TypeError, () => instance.subtract(Symbol()), "Symbol");
+assert.throws(TypeError, () => instance.subtract(7), "number");
+assert.throws(TypeError, () => instance.subtract(7n), "bigint");
+assert.throws(TypeError, () => instance.subtract([]), "array");
+assert.throws(TypeError, () => instance.subtract(() => {}), "function");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-object.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-object.js
new file mode 100644
index 0000000000..38290a6bf9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-object.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: Plain object arguments are supported.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(15, 23, 30, 123, 456, 789);
+TemporalHelpers.assertPlainTime(plainTime.subtract({ hours: 16 }),
+ 23, 23, 30, 123, 456, 789, "subtract 16 hours across midnight boundary");
+TemporalHelpers.assertPlainTime(plainTime.subtract({ minutes: 45 }),
+ 14, 38, 30, 123, 456, 789, "subtract 45 minutes");
+TemporalHelpers.assertPlainTime(plainTime.subtract({ seconds: 45 }),
+ 15, 22, 45, 123, 456, 789, "subtract 45 seconds");
+TemporalHelpers.assertPlainTime(plainTime.subtract({ milliseconds: 800 }),
+ 15, 23, 29, 323, 456, 789, "subtract 800 milliseconds");
+TemporalHelpers.assertPlainTime(plainTime.subtract({ microseconds: 800 }),
+ 15, 23, 30, 122, 656, 789, "subtract 800 microseconds");
+TemporalHelpers.assertPlainTime(plainTime.subtract({ nanoseconds: 800 }),
+ 15, 23, 30, 123, 455, 989, "subtract 800 nanoseconds");
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("23:23:30.123456789").subtract({ hours: -16 }),
+ 15, 23, 30, 123, 456, 789, "subtract -16 hours across midnight boundary");
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("14:38:30.123456789").subtract({ minutes: -45 }),
+ 15, 23, 30, 123, 456, 789, "subtract -45 minutes");
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("15:22:45.123456789").subtract({ seconds: -45 }),
+ 15, 23, 30, 123, 456, 789, "subtract -45 seconds");
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("15:23:29.323456789").subtract({ milliseconds: -800 }),
+ 15, 23, 30, 123, 456, 789, "subtract -800 milliseconds");
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("15:23:30.122656789").subtract({ microseconds: -800 }),
+ 15, 23, 30, 123, 456, 789, "subtract -800 microseconds");
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("15:23:30.123455989").subtract({ nanoseconds: -800 }),
+ 15, 23, 30, 123, 456, 789, "subtract -800 nanoseconds");
+TemporalHelpers.assertPlainTime(plainTime.subtract({ minute: 1, hours: 1 }),
+ 14, 23, 30, 123, 456, 789, "misspelled property is ignored");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-singular-properties.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-singular-properties.js
new file mode 100644
index 0000000000..6bb1a76f9f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-singular-properties.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: Singular properties in the property bag are always ignored
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(15, 30, 45, 987, 654, 321);
+
+[
+ { year: 1 },
+ { month: 2 },
+ { week: 3 },
+ { day: 4 },
+ { hour: 5 },
+ { minute: 6 },
+ { second: 7 },
+ { millisecond: 8 },
+ { microsecond: 9 },
+ { nanosecond: 10 },
+].forEach((badObject) => {
+ assert.throws(TypeError, () => instance.subtract(badObject),
+ "Throw TypeError if temporalDurationLike is not valid");
+});
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-duration-too-large.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-duration-too-large.js
new file mode 100644
index 0000000000..4d26a245df
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-duration-too-large.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: >
+ ParseTemporalDurationString throws a RangeError when the result is too large.
+features: [Temporal]
+---*/
+
+// Number string too long to be representable as a Number value.
+var ones = "1".repeat(1000);
+assert.sameValue(Number(ones), Infinity);
+
+var time = new Temporal.PlainTime();
+var str = "PT" + ones + "S";
+
+assert.throws(RangeError, () => time.subtract(str));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-fractional-units-rounding-mode.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-fractional-units-rounding-mode.js
new file mode 100644
index 0000000000..c0b706f506
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-fractional-units-rounding-mode.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: Strings with fractional duration units are rounded with the correct rounding mode
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const midnight = new Temporal.PlainTime();
+
+TemporalHelpers.assertPlainTime(midnight.subtract("PT1.03125H"), 22, 58, 7, 500, 0, 0,
+ "positive fractional units rounded with correct rounding mode");
+TemporalHelpers.assertPlainTime(midnight.subtract("-PT1.03125H"), 1, 1, 52, 500, 0, 0,
+ "negative fractional units rounded with correct rounding mode");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-negative-fractional-units.js
new file mode 100644
index 0000000000..d11506a0cc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-negative-fractional-units.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+
+const resultHours = instance.subtract("-PT24.567890123H");
+TemporalHelpers.assertPlainTime(resultHours, 0, 34, 4, 404, 442, 800, "negative fractional hours");
+
+const resultMinutes = instance.subtract("-PT1440.567890123M");
+TemporalHelpers.assertPlainTime(resultMinutes, 0, 0, 34, 73, 407, 380, "negative fractional minutes");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string.js
new file mode 100644
index 0000000000..5e05b5bae6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: A string is parsed into the correct object when passed as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = Temporal.PlainTime.from({ hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+const result = instance.subtract("PT3M");
+TemporalHelpers.assertPlainTime(result, 12, 31, 56, 987, 654, 321);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/balance-negative-time-units.js
new file mode 100644
index 0000000000..f0712a3d66
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/balance-negative-time-units.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-addtime step 8:
+ 8. Return ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal.plaintime.prototype.subtract step 4:
+ 4. Let _result_ be ? AddTime(_temporalTime_.[[ISOHour]], _temporalTime_.[[ISOMinute]], _temporalTime_.[[ISOSecond]], _temporalTime_.[[ISOMillisecond]], _temporalTime_.[[ISOMicrosecond]], _temporalTime_.[[ISONanosecond]], −_duration_.[[Hours]], −_duration_.[[Minutes]], −_duration_.[[Seconds]], −_duration_.[[Milliseconds]], −_duration_.[[Microseconds]], −_duration_.[[Nanoseconds]]).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(1, 1, 1, 1, 1, 1);
+
+const result1 = time.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainTime(result1, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
+
+const result2 = time.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainTime(result2, 1, 1, 1, 0, 999, 1, "microseconds balance");
+
+const result3 = time.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainTime(result3, 1, 1, 0, 999, 1, 1, "milliseconds balance");
+
+const result4 = time.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainTime(result4, 1, 0, 59, 1, 1, 1, "seconds balance");
+
+const result5 = time.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainTime(result5, 0, 59, 1, 1, 1, 1, "minutes balance");
+
+const result6 = time.subtract(new Temporal.Duration(0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainTime(result6, 23, 1, 1, 1, 1, 1, "hours mod 24");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/branding.js
new file mode 100644
index 0000000000..c51391219d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const subtract = Temporal.PlainTime.prototype.subtract;
+
+assert.sameValue(typeof subtract, "function");
+
+const args = [new Temporal.Duration(0, 0, 0, 0, 5)];
+
+assert.throws(TypeError, () => subtract.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => subtract.apply(null, args), "null");
+assert.throws(TypeError, () => subtract.apply(true, args), "true");
+assert.throws(TypeError, () => subtract.apply("", args), "empty string");
+assert.throws(TypeError, () => subtract.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => subtract.apply(1, args), "1");
+assert.throws(TypeError, () => subtract.apply({}, args), "plain object");
+assert.throws(TypeError, () => subtract.apply(Temporal.PlainTime, args), "Temporal.PlainTime");
+assert.throws(TypeError, () => subtract.apply(Temporal.PlainTime.prototype, args), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/builtin.js
new file mode 100644
index 0000000000..ee2826cf6d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: >
+ Tests that Temporal.PlainTime.prototype.subtract
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainTime.prototype.subtract),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.prototype.subtract),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.prototype.subtract),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.prototype.subtract.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..27a8ae2ee7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainTime.prototype.subtract throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.plaintime.prototype.subtract
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainTime.from({ hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/length.js
new file mode 100644
index 0000000000..14a130a438
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: Temporal.PlainTime.prototype.subtract.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.subtract, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/name.js
new file mode 100644
index 0000000000..3bc47f8d59
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: Temporal.PlainTime.prototype.subtract.name is "subtract".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.subtract, "name", {
+ value: "subtract",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..e9f557b0ee
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainTime.prototype.subtract throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.plaintime.prototype.subtract
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainTime.from({ hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: -Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/non-integer-throws-rangeerror.js
new file mode 100644
index 0000000000..fd5246180e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/non-integer-throws-rangeerror.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(15, 30, 45, 987, 654, 321);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.subtract({ [field]: -1.5 }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/not-a-constructor.js
new file mode 100644
index 0000000000..c382bb0251
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: >
+ Temporal.PlainTime.prototype.subtract does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainTime.prototype.subtract();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.prototype.subtract), false,
+ "isConstructor(Temporal.PlainTime.prototype.subtract)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/options-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/options-ignored.js
new file mode 100644
index 0000000000..49d07b34ef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/options-ignored.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: Options argument is ignored.
+includes: [temporalHelpers.js]
+features: [Symbol, Temporal]
+---*/
+
+const values = [
+ undefined,
+ null,
+ true,
+ "hello",
+ Symbol("foo"),
+ 1,
+ 1n,
+ {},
+ () => {},
+ { get overflow() { throw new Test262Error("should not get overflow") } },
+];
+
+const time = Temporal.PlainTime.from("15:23:30.123456789");
+for (const options of values) {
+ TemporalHelpers.assertPlainTime(time.subtract({ hours: 1 }, options),
+ 14, 23, 30, 123, 456, 789);
+}
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/order-of-operations.js
new file mode 100644
index 0000000000..c8c81fcc3b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/order-of-operations.js
@@ -0,0 +1,62 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: Properties on an object passed to subtract() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const expected = [
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.months.valueOf",
+ "call fields.months.valueOf",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.weeks.valueOf",
+ "call fields.weeks.valueOf",
+ "get fields.years",
+ "get fields.years.valueOf",
+ "call fields.years.valueOf",
+];
+const actual = [];
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+const result = instance.subtract(fields);
+TemporalHelpers.assertPlainTime(result, 11, 33, 55, 986, 653, 320);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/precision-exact-mathematical-values-1.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/precision-exact-mathematical-values-1.js
new file mode 100644
index 0000000000..3b9836b044
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/precision-exact-mathematical-values-1.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: >
+ Duration components are precise mathematical integers.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let duration = Temporal.Duration.from({
+ microseconds: Number.MIN_SAFE_INTEGER,
+ nanoseconds: -1000,
+});
+
+let time = Temporal.PlainTime.from({
+ microsecond: 1,
+});
+
+let result = time.subtract(duration);
+
+TemporalHelpers.assertPlainTime(result, 23, 47, 34, 740, 993, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/precision-exact-mathematical-values-2.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/precision-exact-mathematical-values-2.js
new file mode 100644
index 0000000000..c216bd6231
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/precision-exact-mathematical-values-2.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: >
+ Duration components are precise mathematical integers.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let duration = Temporal.Duration.from({
+ seconds: -Number.MAX_SAFE_INTEGER,
+ nanoseconds: -999_999_999,
+});
+
+let time = new Temporal.PlainTime(0, 0, 0, 0, 0, 0);
+
+let result = time.subtract(duration);
+
+TemporalHelpers.assertPlainTime(result, 7, 36, 31, 999, 999, 999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/prop-desc.js
new file mode 100644
index 0000000000..7d4e360a6e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: The "subtract" property of Temporal.PlainTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.prototype.subtract,
+ "function",
+ "`typeof PlainTime.prototype.subtract` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime.prototype, "subtract", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/subclassing-ignored.js
new file mode 100644
index 0000000000..e9c47a0666
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/subclassing-ignored.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: Objects of a subclass are never created as return values for subtract()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainTime,
+ [12, 34, 56, 987, 654, 321],
+ "subtract",
+ [{ nanoseconds: 1 }],
+ (result) => TemporalHelpers.assertPlainTime(result, 12, 34, 56, 987, 654, 320),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/basic.js
new file mode 100644
index 0000000000..e3cbd04d1e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/basic.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tojson
+description: Basic behavior for toJSON
+features: [Temporal]
+---*/
+
+const tests = [
+ [new Temporal.PlainTime(5, 3, 1), "05:03:01"],
+ [new Temporal.PlainTime(15, 23), "15:23:00"],
+ [new Temporal.PlainTime(15, 23, 30), "15:23:30"],
+ [new Temporal.PlainTime(15, 23, 30, 123, 400), "15:23:30.1234"],
+];
+
+const options = new Proxy({}, {
+ get() { throw new Test262Error("should not get properties off argument") }
+});
+for (const [time, expected] of tests) {
+ assert.sameValue(time.toJSON(), expected, "toJSON without argument");
+ assert.sameValue(time.toJSON(options), expected, "toJSON with argument");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/branding.js
new file mode 100644
index 0000000000..21624123db
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tojson
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toJSON = Temporal.PlainTime.prototype.toJSON;
+
+assert.sameValue(typeof toJSON, "function");
+
+assert.throws(TypeError, () => toJSON.call(undefined), "undefined");
+assert.throws(TypeError, () => toJSON.call(null), "null");
+assert.throws(TypeError, () => toJSON.call(true), "true");
+assert.throws(TypeError, () => toJSON.call(""), "empty string");
+assert.throws(TypeError, () => toJSON.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toJSON.call(1), "1");
+assert.throws(TypeError, () => toJSON.call({}), "plain object");
+assert.throws(TypeError, () => toJSON.call(Temporal.PlainTime), "Temporal.PlainTime");
+assert.throws(TypeError, () => toJSON.call(Temporal.PlainTime.prototype), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/builtin.js
new file mode 100644
index 0000000000..c2a661a303
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tojson
+description: >
+ Tests that Temporal.PlainTime.prototype.toJSON
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainTime.prototype.toJSON),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.prototype.toJSON),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.prototype.toJSON),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.prototype.toJSON.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/length.js
new file mode 100644
index 0000000000..5c4ca090f3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tojson
+description: Temporal.PlainTime.prototype.toJSON.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.toJSON, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/name.js
new file mode 100644
index 0000000000..60560473d5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tojson
+description: Temporal.PlainTime.prototype.toJSON.name is "toJSON".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.toJSON, "name", {
+ value: "toJSON",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/not-a-constructor.js
new file mode 100644
index 0000000000..ef77906631
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tojson
+description: >
+ Temporal.PlainTime.prototype.toJSON does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainTime.prototype.toJSON();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.prototype.toJSON), false,
+ "isConstructor(Temporal.PlainTime.prototype.toJSON)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/prop-desc.js
new file mode 100644
index 0000000000..56d7e40457
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tojson
+description: The "toJSON" property of Temporal.PlainTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.prototype.toJSON,
+ "function",
+ "`typeof PlainTime.prototype.toJSON` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime.prototype, "toJSON", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/branding.js
new file mode 100644
index 0000000000..2401d31297
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tolocalestring
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toLocaleString = Temporal.PlainTime.prototype.toLocaleString;
+
+assert.sameValue(typeof toLocaleString, "function");
+
+assert.throws(TypeError, () => toLocaleString.call(undefined), "undefined");
+assert.throws(TypeError, () => toLocaleString.call(null), "null");
+assert.throws(TypeError, () => toLocaleString.call(true), "true");
+assert.throws(TypeError, () => toLocaleString.call(""), "empty string");
+assert.throws(TypeError, () => toLocaleString.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toLocaleString.call(1), "1");
+assert.throws(TypeError, () => toLocaleString.call({}), "plain object");
+assert.throws(TypeError, () => toLocaleString.call(Temporal.PlainTime), "Temporal.PlainTime");
+assert.throws(TypeError, () => toLocaleString.call(Temporal.PlainTime.prototype), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/builtin.js
new file mode 100644
index 0000000000..5cf924222c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tolocalestring
+description: >
+ Tests that Temporal.PlainTime.prototype.toLocaleString
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainTime.prototype.toLocaleString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.prototype.toLocaleString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.prototype.toLocaleString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.prototype.toLocaleString.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/length.js
new file mode 100644
index 0000000000..2da8dbc59d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tolocalestring
+description: Temporal.PlainTime.prototype.toLocaleString.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.toLocaleString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/name.js
new file mode 100644
index 0000000000..c736160c0c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tolocalestring
+description: Temporal.PlainTime.prototype.toLocaleString.name is "toLocaleString".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.toLocaleString, "name", {
+ value: "toLocaleString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/not-a-constructor.js
new file mode 100644
index 0000000000..0e7938a0fc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tolocalestring
+description: >
+ Temporal.PlainTime.prototype.toLocaleString does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainTime.prototype.toLocaleString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.prototype.toLocaleString), false,
+ "isConstructor(Temporal.PlainTime.prototype.toLocaleString)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/prop-desc.js
new file mode 100644
index 0000000000..8ed43ab577
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tolocalestring
+description: The "toLocaleString" property of Temporal.PlainTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.prototype.toLocaleString,
+ "function",
+ "`typeof PlainTime.prototype.toLocaleString` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime.prototype, "toLocaleString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/return-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/return-string.js
new file mode 100644
index 0000000000..59c032d396
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/return-string.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Kate Miháliková. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tolocalestring
+description: >
+ toLocaleString return a string.
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+assert.sameValue(typeof time.toLocaleString("en", { timeStyle: "short" }), "string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..aaf98aa359
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.toPlainDateTime(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..fb8706493b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-calendar-datefromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.toPlainDateTime(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..ba733a7bd9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-constructor-in-calendar-fields.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+assert.throws(RangeError, () => instance.toPlainDateTime(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..671a32ba83
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+ assert.throws(RangeError, () => instance.toPlainDateTime(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-leap-second.js
new file mode 100644
index 0000000000..df8483e7b9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-leap-second.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: Leap second is a valid ISO string for PlainDate
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.toPlainDateTime(arg);
+TemporalHelpers.assertPlainDateTime(
+ result1,
+ 2016, 12, "M12", 31, 12, 34, 56, 987, 654, 321,
+ "leap second is a valid ISO string for PlainDate"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.toPlainDateTime(arg);
+TemporalHelpers.assertPlainDateTime(
+ result2,
+ 2016, 12, "M12", 31, 12, 34, 56, 987, 654, 321,
+ "second: 60 is ignored in property bag for PlainDate"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-number.js
new file mode 100644
index 0000000000..aee435340f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-number.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.toPlainDateTime(arg),
+ 'Numbers cannot be used in place of an ISO string for PlainDate'
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-plaindatetime.js
new file mode 100644
index 0000000000..2d5bdfb37e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-plaindatetime.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.toplaindatetime
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainDate by reading internal slots
+info: |
+ sec-temporal.plaintime.prototype.toplaindatetime step 3:
+ 3. Set _temporalDate_ to ? ToTemporalDate(_temporalDate_).
+ sec-temporal-totemporaldate step 2.b:
+ b. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ i. Return ! CreateTemporalDate(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const time = new Temporal.PlainTime(6, 54, 32, 123, 456, 789);
+ const result = time.toPlainDateTime(datetime);
+ TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 6, 54, 32, 123, 456, 789);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..34dd11bf93
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: The calendar name is case-insensitive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.toPlainDateTime(arg);
+TemporalHelpers.assertPlainDateTime(result, 1976, 11, "M11", 18, 12, 34, 56, 987, 654, 321, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..9beb64271e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: Leap second is a valid ISO string for a calendar in a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.toPlainDateTime(arg);
+TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 12, 34, 56, 987, 654, 321,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..407773adb4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-number.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.toPlainDateTime(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..6edf8a1789
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-string.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.toPlainDateTime(arg);
+TemporalHelpers.assertPlainDateTime(result, 1976, 11, "M11", 18, 12, 34, 56, 987, 654, 321, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..7924805b00
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.toPlainDateTime(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.toPlainDateTime(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..f9ba398d5f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-year-zero.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toPlainDateTime(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..fd70a10ff5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-proto-in-calendar-fields.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+assert.throws(RangeError, () => instance.toPlainDateTime(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..b7ab3b08c0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-calendar-annotation.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.toPlainDateTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 321,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..88e313cf0f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!foo=bar]",
+ "1970-01-01T00:00[!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toPlainDateTime(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..6e64c39c15
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-date-with-utc-offset.js
@@ -0,0 +1,49 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.toPlainDateTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 321,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+const invalidStrings = [
+ "2022-09-15Z",
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.toPlainDateTime(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-invalid.js
new file mode 100644
index 0000000000..519b9ff9be
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-invalid.js
@@ -0,0 +1,64 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.toPlainDateTime(arg),
+ `"${arg}" should not be a valid ISO string for a PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..2208411476
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-multiple-calendar.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toPlainDateTime(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..4f47d2457f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-multiple-time-zone.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[UTC][UTC]",
+ "1970-01-01T00:00[UTC][UTC]",
+ "1970-01-01T00:00[!UTC][UTC]",
+ "1970-01-01T00:00[UTC][!UTC]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00[UTC][foo=bar][UTC]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toPlainDateTime(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-time-separators.js
new file mode 100644
index 0000000000..8325711849
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-time-separators.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.toPlainDateTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 321,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..9af036f12c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-time-zone-annotation.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[Asia/Kolkata]", "named, with no time"],
+ ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"],
+ ["2000-05-02[+00:00]", "numeric, with no time"],
+ ["2000-05-02[!-02:30]", "numeric, with ! and no time"],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.toPlainDateTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 321,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..0fb9958f8a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-unknown-annotation.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.toPlainDateTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 321,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..4e47c1b5cd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-with-utc-designator.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toPlainDateTime(arg),
+ "String with UTC designator should not be valid as a PlainDate"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-wrong-type.js
new file mode 100644
index 0000000000..95387bf637
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-wrong-type.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => instance.toPlainDateTime(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.toPlainDateTime(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-convert.js
new file mode 100644
index 0000000000..39f6296bda
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+assert.throws(Test262Error, () => instance.toPlainDateTime(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..a7ea2c308e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-slots.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+instance.toPlainDateTime(arg);
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..996ac97964
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => time.toPlainDateTime(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..5757a1d253
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => time.toPlainDateTime(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..92bff3ca6d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => time.toPlainDateTime(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..b1b91f30fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => time.toPlainDateTime(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/basic.js
new file mode 100644
index 0000000000..995467100a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/basic.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.toplaindatetime
+description: Basic tests for toPlainDateTime().
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = Temporal.PlainTime.from("11:30:23.123456789");
+
+const plainDate = plainTime.toPlainDateTime(Temporal.PlainDate.from("1976-11-18"));
+TemporalHelpers.assertPlainDateTime(plainDate, 1976, 11, "M11", 18, 11, 30, 23, 123, 456, 789, "PlainDate");
+
+const optionBag = plainTime.toPlainDateTime({ year: 1976, month: 11, day: 18 });
+TemporalHelpers.assertPlainDateTime(optionBag, 1976, 11, "M11", 18, 11, 30, 23, 123, 456, 789, "option bag");
+
+const string = plainTime.toPlainDateTime("1976-11-18");
+TemporalHelpers.assertPlainDateTime(string, 1976, 11, "M11", 18, 11, 30, 23, 123, 456, 789, "string");
+
+assert.throws(TypeError, () => plainTime.toPlainDateTime({ year: 1976 }), "missing properties");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/branding.js
new file mode 100644
index 0000000000..724c37070f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toPlainDateTime = Temporal.PlainTime.prototype.toPlainDateTime;
+
+assert.sameValue(typeof toPlainDateTime, "function");
+
+const args = [new Temporal.PlainDate(2022, 6, 22)];
+
+assert.throws(TypeError, () => toPlainDateTime.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => toPlainDateTime.apply(null, args), "null");
+assert.throws(TypeError, () => toPlainDateTime.apply(true, args), "true");
+assert.throws(TypeError, () => toPlainDateTime.apply("", args), "empty string");
+assert.throws(TypeError, () => toPlainDateTime.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => toPlainDateTime.apply(1, args), "1");
+assert.throws(TypeError, () => toPlainDateTime.apply({}, args), "plain object");
+assert.throws(TypeError, () => toPlainDateTime.apply(Temporal.PlainTime, args), "Temporal.PlainTime");
+assert.throws(TypeError, () => toPlainDateTime.apply(Temporal.PlainTime.prototype, args), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/builtin.js
new file mode 100644
index 0000000000..9242d0b4a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: >
+ Tests that Temporal.PlainTime.prototype.toPlainDateTime
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainTime.prototype.toPlainDateTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.prototype.toPlainDateTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.prototype.toPlainDateTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.prototype.toPlainDateTime.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..cbca1c086f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-datefromfields-called-with-options-undefined.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321, calendar);
+instance.toPlainDateTime({ year: 2000, month: 5, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-fields-iterable.js
new file mode 100644
index 0000000000..93ae2c6dc0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-fields-iterable.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaintime.prototype.toplaindatetime step 3:
+ 3. Set _temporalDate_ to ? ToTemporalDate(_temporalDate_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const time = new Temporal.PlainTime(13, 3);
+const calendar = TemporalHelpers.calendarFieldsIterable();
+time.toPlainDateTime({ year: 2000, month: 5, day: 3, calendar });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-temporal-object.js
new file mode 100644
index 0000000000..9ccbd4fc03
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-temporal-object.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaintime.prototype.toplaindatetime step 3:
+ 3. Set _temporalDate_ to ? ToTemporalDate(_temporalDate_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => {
+ const time = new Temporal.PlainTime(13, 3);
+ const result = time.toPlainDateTime({ year: 2000, month: 5, day: 3, calendar: temporalObject });
+ assert.sameValue(result.getCalendar(), calendar, "Temporal object coerced to calendar");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..84a2e311a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/infinity-throws-rangeerror.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(15);
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.toPlainDateTime({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.toPlainDateTime({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/length.js
new file mode 100644
index 0000000000..a10aff33cb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: Temporal.PlainTime.prototype.toPlainDateTime.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.toPlainDateTime, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/limits.js
new file mode 100644
index 0000000000..d2561e05c6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/limits.js
@@ -0,0 +1,42 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: Checking limits of representable PlainDateTime
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const midnight = new Temporal.PlainTime(0, 0);
+const firstNs = new Temporal.PlainTime(0, 0, 0, 0, 0, 1);
+const lastNs = new Temporal.PlainTime(23, 59, 59, 999, 999, 999);
+const min = new Temporal.PlainDate(-271821, 4, 19);
+const max = new Temporal.PlainDate(275760, 9, 13);
+
+assert.throws(
+ RangeError,
+ () => midnight.toPlainDateTime(min),
+ "Cannot go below representable limit"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ midnight.toPlainDateTime(max),
+ 275760, 9, "M09", 13, 0, 0, 0, 0, 0, 0,
+ "Midnight of maximum representable PlainDate"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ firstNs.toPlainDateTime(min),
+ -271821, 4, "M04", 19, 0, 0, 0, 0, 0, 1,
+ "Computing the minimum (earliest) representable PlainDateTime"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ lastNs.toPlainDateTime(max),
+ 275760, 9, "M09", 13, 23, 59, 59, 999, 999, 999,
+ "Computing the maximum (latest) representable PlainDateTime"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/name.js
new file mode 100644
index 0000000000..830ca10999
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: Temporal.PlainTime.prototype.toPlainDateTime.name is "toPlainDateTime".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.toPlainDateTime, "name", {
+ value: "toPlainDateTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/not-a-constructor.js
new file mode 100644
index 0000000000..38b8e598ef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: >
+ Temporal.PlainTime.prototype.toPlainDateTime does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainTime.prototype.toPlainDateTime();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.prototype.toPlainDateTime), false,
+ "isConstructor(Temporal.PlainTime.prototype.toPlainDateTime)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/prop-desc.js
new file mode 100644
index 0000000000..ecc3c8e55a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: The "toPlainDateTime" property of Temporal.PlainTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.prototype.toPlainDateTime,
+ "function",
+ "`typeof PlainTime.prototype.toPlainDateTime` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime.prototype, "toPlainDateTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/year-zero.js
new file mode 100644
index 0000000000..090c19d1eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/year-zero.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toPlainDateTime(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/basic.js
new file mode 100644
index 0000000000..d43372e50f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/basic.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: Basic tests for toString()
+features: [Temporal]
+---*/
+
+assert.sameValue(new Temporal.PlainTime(15, 23).toString(), "15:23:00");
+assert.sameValue(new Temporal.PlainTime(15, 23, 30).toString(), "15:23:30");
+assert.sameValue(new Temporal.PlainTime(15, 23, 30, 123).toString(), "15:23:30.123");
+assert.sameValue(new Temporal.PlainTime(15, 23, 30, 123, 400).toString(), "15:23:30.1234");
+assert.sameValue(new Temporal.PlainTime(15, 23, 30, 123, 456).toString(), "15:23:30.123456");
+assert.sameValue(new Temporal.PlainTime(15, 23, 30, 123, 456, 789).toString(), "15:23:30.123456789");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/branding.js
new file mode 100644
index 0000000000..cd2d2e6171
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toString = Temporal.PlainTime.prototype.toString;
+
+assert.sameValue(typeof toString, "function");
+
+assert.throws(TypeError, () => toString.call(undefined), "undefined");
+assert.throws(TypeError, () => toString.call(null), "null");
+assert.throws(TypeError, () => toString.call(true), "true");
+assert.throws(TypeError, () => toString.call(""), "empty string");
+assert.throws(TypeError, () => toString.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toString.call(1), "1");
+assert.throws(TypeError, () => toString.call({}), "plain object");
+assert.throws(TypeError, () => toString.call(Temporal.PlainTime), "Temporal.PlainTime");
+assert.throws(TypeError, () => toString.call(Temporal.PlainTime.prototype), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/builtin.js
new file mode 100644
index 0000000000..c540a97479
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: >
+ Tests that Temporal.PlainTime.prototype.toString
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainTime.prototype.toString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.prototype.toString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.prototype.toString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.prototype.toString.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-auto.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-auto.js
new file mode 100644
index 0000000000..4468ac3f39
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-auto.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: auto value for fractionalSecondDigits option
+features: [Temporal]
+---*/
+
+const tests = [
+ [new Temporal.PlainTime(5, 3, 1), "05:03:01"],
+ [new Temporal.PlainTime(15, 23), "15:23:00"],
+ [new Temporal.PlainTime(15, 23, 30), "15:23:30"],
+ [new Temporal.PlainTime(15, 23, 30, 123, 400), "15:23:30.1234"],
+];
+
+for (const [time, expected] of tests) {
+ assert.sameValue(time.toString(), expected, "default is to emit seconds and drop trailing zeroes");
+ assert.sameValue(time.toString({ fractionalSecondDigits: "auto" }), expected, "auto is the default");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-invalid-string.js
new file mode 100644
index 0000000000..9f6d4b7454
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-invalid-string.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option not one of the allowed string values
+info: |
+ sec-getstringornumberoption step 4:
+ 4. If _stringValues_ is not *undefined* and _stringValues_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaintime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 650, 0);
+
+for (const fractionalSecondDigits of ["other string", "AUTO", "not-auto", "autos", "auto\0"]) {
+ assert.throws(RangeError, () => time.toString({ fractionalSecondDigits }),
+ `"${fractionalSecondDigits}" is not a valid value for fractionalSecondDigits`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-nan.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-nan.js
new file mode 100644
index 0000000000..ec1a8a2903
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-nan.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaintime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 650, 0);
+assert.throws(RangeError, () => time.toString({ fractionalSecondDigits: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-non-integer.js
new file mode 100644
index 0000000000..2a0eefadbf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-non-integer.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: Rounding for fractionalSecondDigits option
+info: |
+ sec-getstringornumberoption step 3.b:
+ b. Return floor(ℝ(_value_)).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaintime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 650, 0);
+
+let string = time.toString({ fractionalSecondDigits: 2.5 });
+assert.sameValue(string, "12:34:56.98", "fractionalSecondDigits 2.5 floors to 2");
+
+string = time.toString({ fractionalSecondDigits: 9.7 });
+assert.sameValue(string, "12:34:56.987650000", "fractionalSecondDigits 9.7 floors to 9 and is not out of range");
+
+assert.throws(
+ RangeError,
+ () => time.toString({ fractionalSecondDigits: -0.6 }),
+ "fractionalSecondDigits -0.6 floors to -1 and is out of range"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-number.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-number.js
new file mode 100644
index 0000000000..068e608617
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-number.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: Number for fractionalSecondDigits option
+features: [Temporal]
+---*/
+
+const fewSeconds = new Temporal.PlainTime(5, 3, 1);
+const zeroSeconds = new Temporal.PlainTime(15, 23);
+const wholeSeconds = new Temporal.PlainTime(15, 23, 30);
+const subSeconds = new Temporal.PlainTime(15, 23, 30, 123, 400);
+
+assert.sameValue(fewSeconds.toString({ fractionalSecondDigits: 0 }), "05:03:01",
+ "pads parts with 0");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 0 }), "15:23:30",
+ "truncates 4 decimal places to 0");
+assert.sameValue(zeroSeconds.toString({ fractionalSecondDigits: 2 }), "15:23:00.00",
+ "pads zero seconds to 2 decimal places");
+assert.sameValue(wholeSeconds.toString({ fractionalSecondDigits: 2 }), "15:23:30.00",
+ "pads whole seconds to 2 decimal places");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 2 }), "15:23:30.12",
+ "truncates 4 decimal places to 2");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 3 }), "15:23:30.123",
+ "truncates 4 decimal places to 3");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 6 }), "15:23:30.123400",
+ "pads 4 decimal places to 6");
+assert.sameValue(zeroSeconds.toString({ fractionalSecondDigits: 7 }), "15:23:00.0000000",
+ "pads zero seconds to 7 decimal places");
+assert.sameValue(wholeSeconds.toString({ fractionalSecondDigits: 7 }), "15:23:30.0000000",
+ "pads whole seconds to 7 decimal places");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 7 }), "15:23:30.1234000",
+ "pads 4 decimal places to 7");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 9 }), "15:23:30.123400000",
+ "pads 4 decimal places to 9");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-out-of-range.js
new file mode 100644
index 0000000000..ca1a79ee64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-out-of-range.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option out of range
+info: |
+ sec-getstringornumberoption step 3.a:
+ a. If _value_ < _minimum_ or _value_ > _maximum_, throw a *RangeError* exception.
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaintime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 650, 0);
+
+assert.throws(RangeError, () => time.toString({ fractionalSecondDigits: -Infinity }),
+ "−∞ is out of range for fractionalSecondDigits");
+assert.throws(RangeError, () => time.toString({ fractionalSecondDigits: -1 }),
+ "−1 is out of range for fractionalSecondDigits");
+assert.throws(RangeError, () => time.toString({ fractionalSecondDigits: 10 }),
+ "10 is out of range for fractionalSecondDigits");
+assert.throws(RangeError, () => time.toString({ fractionalSecondDigits: Infinity }),
+ "∞ is out of range for fractionalSecondDigits");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-undefined.js
new file mode 100644
index 0000000000..c7b073dfac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-undefined.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: Fallback value for fractionalSecondDigits option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaintime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const tests = [
+ [new Temporal.PlainTime(5, 3, 1), "05:03:01"],
+ [new Temporal.PlainTime(15, 23), "15:23:00"],
+ [new Temporal.PlainTime(15, 23, 30), "15:23:30"],
+ [new Temporal.PlainTime(15, 23, 30, 123, 400), "15:23:30.1234"],
+];
+
+for (const [time, expected] of tests) {
+ const explicit = time.toString({ fractionalSecondDigits: undefined });
+ assert.sameValue(explicit, expected, "default fractionalSecondDigits is auto (property present but undefined)");
+
+ const implicit = time.toString({});
+ assert.sameValue(implicit, expected, "default fractionalSecondDigits is auto (property not present)");
+
+ const lambda = time.toString(() => {});
+ assert.sameValue(lambda, expected, "default fractionalSecondDigits is auto (property not present, function object)");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-wrong-type.js
new file mode 100644
index 0000000000..ff611b4652
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-wrong-type.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: Type conversions for fractionalSecondDigits option
+info: |
+ sec-getoption steps 8–9:
+ 8. Else if _type_ is Number, then
+ a. Set _value_ to ? ToNumber(value).
+ b. ...
+ 9. Else,
+ a. Set _value_ to ? ToString(value).
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaintime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 650, 0);
+
+assert.throws(RangeError, () => time.toString({ fractionalSecondDigits: null }),
+ "null is not a number and converts to the string 'null' which is not valid for fractionalSecondDigits");
+assert.throws(RangeError, () => time.toString({ fractionalSecondDigits: true }),
+ "true is not a number and converts to the string 'true' which is not valid for fractionalSecondDigits");
+assert.throws(RangeError, () => time.toString({ fractionalSecondDigits: false }),
+ "false is not a number and converts to the string 'false' which is not valid for fractionalSecondDigits");
+assert.throws(TypeError, () => time.toString({ fractionalSecondDigits: Symbol() }),
+ "symbols are not numbers and cannot convert to strings");
+assert.throws(RangeError, () => time.toString({ fractionalSecondDigits: 2n }),
+ "bigints are not numbers and convert to strings which are not valid for fractionalSecondDigits");
+assert.throws(RangeError, () => time.toString({ fractionalSecondDigits: {} }),
+ "plain objects are not numbers and convert to strings which are not valid for fractionalSecondDigits");
+
+const expected = [
+ "get fractionalSecondDigits.toString",
+ "call fractionalSecondDigits.toString",
+];
+const actual = [];
+const observer = TemporalHelpers.toPrimitiveObserver(actual, "auto", "fractionalSecondDigits");
+const result = time.toString({ fractionalSecondDigits: observer });
+assert.sameValue(result, "12:34:56.98765", "object with toString uses toString return value");
+assert.compareArray(actual, expected, "object with toString calls toString and not valueOf");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/length.js
new file mode 100644
index 0000000000..d52b57ed79
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: Temporal.PlainTime.prototype.toString.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.toString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/name.js
new file mode 100644
index 0000000000..7e418da574
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: Temporal.PlainTime.prototype.toString.name is "toString".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.toString, "name", {
+ value: "toString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/not-a-constructor.js
new file mode 100644
index 0000000000..0dc5dd1963
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: >
+ Temporal.PlainTime.prototype.toString does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainTime.prototype.toString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.prototype.toString), false,
+ "isConstructor(Temporal.PlainTime.prototype.toString)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-invalid.js
new file mode 100644
index 0000000000..5b488efb27
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-invalid.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: TypeError thrown when a primitive is passed as the options argument
+features: [Temporal]
+---*/
+
+const instance = Temporal.PlainTime.from("12:56:32");
+const values = [null, true, "hello", Symbol("foo"), 1, 1n];
+
+for (const badOptions of values) {
+ assert.throws(TypeError, () => instance.toString(badOptions));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-object.js
new file mode 100644
index 0000000000..ff4fd01393
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-object.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: Empty or a function object may be used as options
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+
+const result1 = instance.toString({});
+assert.sameValue(
+ result1, "00:00:00",
+ "options may be an empty plain object"
+);
+
+const result2 = instance.toString(() => {});
+assert.sameValue(
+ result2, "00:00:00",
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-undefined.js
new file mode 100644
index 0000000000..c2f24bac5f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-undefined.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const tests = [
+ ["15:23", "15:23:00"],
+ ["15:23:30", "15:23:30"],
+ ["15:23:30.1234", "15:23:30.1234"],
+];
+
+for (const [input, expected] of tests) {
+ const time = Temporal.PlainTime.from(input);
+
+ const explicit = time.toString(undefined);
+ assert.sameValue(explicit, expected, "default precision is auto and no rounding");
+
+ const implicit = time.toString();
+ assert.sameValue(implicit, expected, "default precision is auto and no rounding");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-wrong-type.js
new file mode 100644
index 0000000000..130b55c581
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-wrong-type.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainTime();
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.toString(value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/order-of-operations.js
new file mode 100644
index 0000000000..d417bc719d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/order-of-operations.js
@@ -0,0 +1,49 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: Properties on objects passed to toString() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get options.fractionalSecondDigits",
+ "get options.fractionalSecondDigits.toString",
+ "call options.fractionalSecondDigits.toString",
+ "get options.roundingMode",
+ "get options.roundingMode.toString",
+ "call options.roundingMode.toString",
+ "get options.smallestUnit",
+];
+const actual = [];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const expectedForSmallestUnit = expected.concat([
+ "get options.smallestUnit.toString",
+ "call options.smallestUnit.toString",
+]);
+
+instance.toString(
+ TemporalHelpers.propertyBagObserver(actual, {
+ fractionalSecondDigits: "auto",
+ roundingMode: "halfExpand",
+ smallestUnit: "millisecond",
+ }, "options"),
+);
+assert.compareArray(actual, expectedForSmallestUnit, "order of operations");
+actual.splice(0); // clear
+
+instance.toString(
+ TemporalHelpers.propertyBagObserver(actual, {
+ fractionalSecondDigits: "auto",
+ roundingMode: "halfExpand",
+ smallestUnit: undefined,
+ }, "options"),
+);
+assert.compareArray(actual, expected, "order of operations with smallestUnit undefined");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/prop-desc.js
new file mode 100644
index 0000000000..10428d6fc5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: The "toString" property of Temporal.PlainTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.prototype.toString,
+ "function",
+ "`typeof PlainTime.prototype.toString` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime.prototype, "toString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/rounding-cross-midnight.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/rounding-cross-midnight.js
new file mode 100644
index 0000000000..8b5b2cb4ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/rounding-cross-midnight.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: Rounding can cross midnight
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(23, 59, 59, 999, 999, 999); // one nanosecond before 00:00:00
+for (const roundingMode of ["ceil", "halfExpand"]) {
+ assert.sameValue(plainTime.toString({ fractionalSecondDigits: 8, roundingMode }), "00:00:00.00000000");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-ceil.js
new file mode 100644
index 0000000000..5d72280641
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-ceil.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: ceil value for roundingMode option
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+
+const result1 = time.toString({ smallestUnit: "microsecond", roundingMode: "ceil" });
+assert.sameValue(result1, "12:34:56.123988",
+ "roundingMode is ceil (with 6 digits from smallestUnit)");
+
+const result2 = time.toString({ fractionalSecondDigits: 6, roundingMode: "ceil" });
+assert.sameValue(result2, "12:34:56.123988",
+ "roundingMode is ceil (with 6 digits from fractionalSecondDigits)");
+
+const result3 = time.toString({ smallestUnit: "millisecond", roundingMode: "ceil" });
+assert.sameValue(result3, "12:34:56.124",
+ "roundingMode is ceil (with 3 digits from smallestUnit)");
+
+const result4 = time.toString({ fractionalSecondDigits: 3, roundingMode: "ceil" });
+assert.sameValue(result4, "12:34:56.124",
+ "roundingMode is ceil (with 3 digits from fractionalSecondDigits)");
+
+const result5 = time.toString({ smallestUnit: "second", roundingMode: "ceil" });
+assert.sameValue(result5, "12:34:57",
+ "roundingMode is ceil (with 0 digits from smallestUnit)");
+
+const result6 = time.toString({ fractionalSecondDigits: 0, roundingMode: "ceil" });
+assert.sameValue(result6, "12:34:57",
+ "roundingMode is ceil (with 0 digits from fractionalSecondDigits)");
+
+const result7 = time.toString({ smallestUnit: "minute", roundingMode: "ceil" });
+assert.sameValue(result7, "12:35", "roundingMode is ceil (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-expand.js
new file mode 100644
index 0000000000..29a4b20c23
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-expand.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: expand value for roundingMode option
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+
+const result1 = time.toString({ smallestUnit: "microsecond", roundingMode: "expand" });
+assert.sameValue(result1, "12:34:56.123988",
+ "roundingMode is expand (with 6 digits from smallestUnit)");
+
+const result2 = time.toString({ fractionalSecondDigits: 6, roundingMode: "expand" });
+assert.sameValue(result2, "12:34:56.123988",
+ "roundingMode is expand (with 6 digits from fractionalSecondDigits)");
+
+const result3 = time.toString({ smallestUnit: "millisecond", roundingMode: "expand" });
+assert.sameValue(result3, "12:34:56.124",
+ "roundingMode is expand (with 3 digits from smallestUnit)");
+
+const result4 = time.toString({ fractionalSecondDigits: 3, roundingMode: "expand" });
+assert.sameValue(result4, "12:34:56.124",
+ "roundingMode is expand (with 3 digits from fractionalSecondDigits)");
+
+const result5 = time.toString({ smallestUnit: "second", roundingMode: "expand" });
+assert.sameValue(result5, "12:34:57",
+ "roundingMode is expand (with 0 digits from smallestUnit)");
+
+const result6 = time.toString({ fractionalSecondDigits: 0, roundingMode: "expand" });
+assert.sameValue(result6, "12:34:57",
+ "roundingMode is expand (with 0 digits from fractionalSecondDigits)");
+
+const result7 = time.toString({ smallestUnit: "minute", roundingMode: "expand" });
+assert.sameValue(result7, "12:35", "roundingMode is expand (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-floor.js
new file mode 100644
index 0000000000..e169a3d814
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-floor.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: floor value for roundingMode option
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+
+const result1 = time.toString({ smallestUnit: "microsecond", roundingMode: "floor" });
+assert.sameValue(result1, "12:34:56.123987",
+ "roundingMode is floor (with 6 digits from smallestUnit)");
+
+const result2 = time.toString({ fractionalSecondDigits: 6, roundingMode: "floor" });
+assert.sameValue(result2, "12:34:56.123987",
+ "roundingMode is floor (with 6 digits from fractionalSecondDigits)");
+
+const result3 = time.toString({ smallestUnit: "millisecond", roundingMode: "floor" });
+assert.sameValue(result3, "12:34:56.123",
+ "roundingMode is floor (with 3 digits from smallestUnit)");
+
+const result4 = time.toString({ fractionalSecondDigits: 3, roundingMode: "floor" });
+assert.sameValue(result4, "12:34:56.123",
+ "roundingMode is floor (with 3 digits from fractionalSecondDigits)");
+
+const result5 = time.toString({ smallestUnit: "second", roundingMode: "floor" });
+assert.sameValue(result5, "12:34:56",
+ "roundingMode is floor (with 0 digits from smallestUnit)");
+
+const result6 = time.toString({ fractionalSecondDigits: 0, roundingMode: "floor" });
+assert.sameValue(result6, "12:34:56",
+ "roundingMode is floor (with 0 digits from fractionalSecondDigits)");
+
+const result7 = time.toString({ smallestUnit: "minute", roundingMode: "floor" });
+assert.sameValue(result7, "12:34", "roundingMode is floor (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..4fa20cfcd4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfCeil.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: halfCeil value for roundingMode option
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+
+const result1 = time.toString({ smallestUnit: "microsecond", roundingMode: "halfCeil" });
+assert.sameValue(result1, "12:34:56.123988",
+ "roundingMode is halfCeil (with 6 digits from smallestUnit)");
+
+const result2 = time.toString({ fractionalSecondDigits: 6, roundingMode: "halfCeil" });
+assert.sameValue(result2, "12:34:56.123988",
+ "roundingMode is halfCeil (with 6 digits from fractionalSecondDigits)");
+
+const result3 = time.toString({ smallestUnit: "millisecond", roundingMode: "halfCeil" });
+assert.sameValue(result3, "12:34:56.124",
+ "roundingMode is halfCeil (with 3 digits from smallestUnit)");
+
+const result4 = time.toString({ fractionalSecondDigits: 3, roundingMode: "halfCeil" });
+assert.sameValue(result4, "12:34:56.124",
+ "roundingMode is halfCeil (with 3 digits from fractionalSecondDigits)");
+
+const result5 = time.toString({ smallestUnit: "second", roundingMode: "halfCeil" });
+assert.sameValue(result5, "12:34:56",
+ "roundingMode is halfCeil (with 0 digits from smallestUnit)");
+
+const result6 = time.toString({ fractionalSecondDigits: 0, roundingMode: "halfCeil" });
+assert.sameValue(result6, "12:34:56",
+ "roundingMode is halfCeil (with 0 digits from fractionalSecondDigits)");
+
+const result7 = time.toString({ smallestUnit: "minute", roundingMode: "halfCeil" });
+assert.sameValue(result7, "12:35", "roundingMode is halfCeil (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfEven.js
new file mode 100644
index 0000000000..4740838f6e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfEven.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: halfEven value for roundingMode option
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+
+const result1 = time.toString({ smallestUnit: "microsecond", roundingMode: "halfEven" });
+assert.sameValue(result1, "12:34:56.123988",
+ "roundingMode is halfEven (with 6 digits from smallestUnit)");
+
+const result2 = time.toString({ fractionalSecondDigits: 6, roundingMode: "halfEven" });
+assert.sameValue(result2, "12:34:56.123988",
+ "roundingMode is halfEven (with 6 digits from fractionalSecondDigits)");
+
+const result3 = time.toString({ smallestUnit: "millisecond", roundingMode: "halfEven" });
+assert.sameValue(result3, "12:34:56.124",
+ "roundingMode is halfEven (with 3 digits from smallestUnit)");
+
+const result4 = time.toString({ fractionalSecondDigits: 3, roundingMode: "halfEven" });
+assert.sameValue(result4, "12:34:56.124",
+ "roundingMode is halfEven (with 3 digits from fractionalSecondDigits)");
+
+const result5 = time.toString({ smallestUnit: "second", roundingMode: "halfEven" });
+assert.sameValue(result5, "12:34:56",
+ "roundingMode is halfEven (with 0 digits from smallestUnit)");
+
+const result6 = time.toString({ fractionalSecondDigits: 0, roundingMode: "halfEven" });
+assert.sameValue(result6, "12:34:56",
+ "roundingMode is halfEven (with 0 digits from fractionalSecondDigits)");
+
+const result7 = time.toString({ smallestUnit: "minute", roundingMode: "halfEven" });
+assert.sameValue(result7, "12:35", "roundingMode is halfEven (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..0cfb80098a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfExpand.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: halfExpand value for roundingMode option
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+
+const result1 = time.toString({ smallestUnit: "microsecond", roundingMode: "halfExpand" });
+assert.sameValue(result1, "12:34:56.123988",
+ "roundingMode is halfExpand (with 6 digits from smallestUnit)");
+
+const result2 = time.toString({ fractionalSecondDigits: 6, roundingMode: "halfExpand" });
+assert.sameValue(result2, "12:34:56.123988",
+ "roundingMode is halfExpand (with 6 digits from fractionalSecondDigits)");
+
+const result3 = time.toString({ smallestUnit: "millisecond", roundingMode: "halfExpand" });
+assert.sameValue(result3, "12:34:56.124",
+ "roundingMode is halfExpand (with 3 digits from smallestUnit)");
+
+const result4 = time.toString({ fractionalSecondDigits: 3, roundingMode: "halfExpand" });
+assert.sameValue(result4, "12:34:56.124",
+ "roundingMode is halfExpand (with 3 digits from fractionalSecondDigits)");
+
+const result5 = time.toString({ smallestUnit: "second", roundingMode: "halfExpand" });
+assert.sameValue(result5, "12:34:56",
+ "roundingMode is halfExpand (with 0 digits from smallestUnit)");
+
+const result6 = time.toString({ fractionalSecondDigits: 0, roundingMode: "halfExpand" });
+assert.sameValue(result6, "12:34:56",
+ "roundingMode is halfExpand (with 0 digits from fractionalSecondDigits)");
+
+const result7 = time.toString({ smallestUnit: "minute", roundingMode: "halfExpand" });
+assert.sameValue(result7, "12:35", "roundingMode is halfExpand (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..e5edab5e8f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfFloor.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: halfFloor value for roundingMode option
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+
+const result1 = time.toString({ smallestUnit: "microsecond", roundingMode: "halfFloor" });
+assert.sameValue(result1, "12:34:56.123987",
+ "roundingMode is halfFloor (with 6 digits from smallestUnit)");
+
+const result2 = time.toString({ fractionalSecondDigits: 6, roundingMode: "halfFloor" });
+assert.sameValue(result2, "12:34:56.123987",
+ "roundingMode is halfFloor (with 6 digits from fractionalSecondDigits)");
+
+const result3 = time.toString({ smallestUnit: "millisecond", roundingMode: "halfFloor" });
+assert.sameValue(result3, "12:34:56.124",
+ "roundingMode is halfFloor (with 3 digits from smallestUnit)");
+
+const result4 = time.toString({ fractionalSecondDigits: 3, roundingMode: "halfFloor" });
+assert.sameValue(result4, "12:34:56.124",
+ "roundingMode is halfFloor (with 3 digits from fractionalSecondDigits)");
+
+const result5 = time.toString({ smallestUnit: "second", roundingMode: "halfFloor" });
+assert.sameValue(result5, "12:34:56",
+ "roundingMode is halfFloor (with 0 digits from smallestUnit)");
+
+const result6 = time.toString({ fractionalSecondDigits: 0, roundingMode: "halfFloor" });
+assert.sameValue(result6, "12:34:56",
+ "roundingMode is halfFloor (with 0 digits from fractionalSecondDigits)");
+
+const result7 = time.toString({ smallestUnit: "minute", roundingMode: "halfFloor" });
+assert.sameValue(result7, "12:35", "roundingMode is halfFloor (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..9c94a5d011
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfTrunc.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: halfTrunc value for roundingMode option
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+
+const result1 = time.toString({ smallestUnit: "microsecond", roundingMode: "halfTrunc" });
+assert.sameValue(result1, "12:34:56.123987",
+ "roundingMode is halfTrunc (with 6 digits from smallestUnit)");
+
+const result2 = time.toString({ fractionalSecondDigits: 6, roundingMode: "halfTrunc" });
+assert.sameValue(result2, "12:34:56.123987",
+ "roundingMode is halfTrunc (with 6 digits from fractionalSecondDigits)");
+
+const result3 = time.toString({ smallestUnit: "millisecond", roundingMode: "halfTrunc" });
+assert.sameValue(result3, "12:34:56.124",
+ "roundingMode is halfTrunc (with 3 digits from smallestUnit)");
+
+const result4 = time.toString({ fractionalSecondDigits: 3, roundingMode: "halfTrunc" });
+assert.sameValue(result4, "12:34:56.124",
+ "roundingMode is halfTrunc (with 3 digits from fractionalSecondDigits)");
+
+const result5 = time.toString({ smallestUnit: "second", roundingMode: "halfTrunc" });
+assert.sameValue(result5, "12:34:56",
+ "roundingMode is halfTrunc (with 0 digits from smallestUnit)");
+
+const result6 = time.toString({ fractionalSecondDigits: 0, roundingMode: "halfTrunc" });
+assert.sameValue(result6, "12:34:56",
+ "roundingMode is halfTrunc (with 0 digits from fractionalSecondDigits)");
+
+const result7 = time.toString({ smallestUnit: "minute", roundingMode: "halfTrunc" });
+assert.sameValue(result7, "12:35", "roundingMode is halfTrunc (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..e59438a859
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-invalid-string.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) {
+ assert.throws(RangeError, () => time.toString({ smallestUnit: "microsecond", roundingMode }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-trunc.js
new file mode 100644
index 0000000000..349ec756fc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-trunc.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: trunc value for roundingMode option
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+
+const result1 = time.toString({ smallestUnit: "microsecond", roundingMode: "trunc" });
+assert.sameValue(result1, "12:34:56.123987",
+ "roundingMode is trunc (with 6 digits from smallestUnit)");
+
+const result2 = time.toString({ fractionalSecondDigits: 6, roundingMode: "trunc" });
+assert.sameValue(result2, "12:34:56.123987",
+ "roundingMode is trunc (with 6 digits from fractionalSecondDigits)");
+
+const result3 = time.toString({ smallestUnit: "millisecond", roundingMode: "trunc" });
+assert.sameValue(result3, "12:34:56.123",
+ "roundingMode is trunc (with 3 digits from smallestUnit)");
+
+const result4 = time.toString({ fractionalSecondDigits: 3, roundingMode: "trunc" });
+assert.sameValue(result4, "12:34:56.123",
+ "roundingMode is trunc (with 3 digits from fractionalSecondDigits)");
+
+const result5 = time.toString({ smallestUnit: "second", roundingMode: "trunc" });
+assert.sameValue(result5, "12:34:56",
+ "roundingMode is trunc (with 0 digits from smallestUnit)");
+
+const result6 = time.toString({ fractionalSecondDigits: 0, roundingMode: "trunc" });
+assert.sameValue(result6, "12:34:56",
+ "roundingMode is trunc (with 0 digits from fractionalSecondDigits)");
+
+const result7 = time.toString({ smallestUnit: "minute", roundingMode: "trunc" });
+assert.sameValue(result7, "12:34", "roundingMode is trunc (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-undefined.js
new file mode 100644
index 0000000000..836cbb0f68
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-undefined.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: Fallback value for roundingMode option
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+
+const explicit1 = time.toString({ smallestUnit: "microsecond", roundingMode: undefined });
+assert.sameValue(explicit1, "12:34:56.123987", "default roundingMode is trunc");
+const implicit1 = time.toString({ smallestUnit: "microsecond" });
+assert.sameValue(implicit1, "12:34:56.123987", "default roundingMode is trunc");
+
+const explicit2 = time.toString({ smallestUnit: "millisecond", roundingMode: undefined });
+assert.sameValue(explicit2, "12:34:56.123", "default roundingMode is trunc");
+const implicit2 = time.toString({ smallestUnit: "millisecond" });
+assert.sameValue(implicit2, "12:34:56.123", "default roundingMode is trunc");
+
+const explicit3 = time.toString({ smallestUnit: "second", roundingMode: undefined });
+assert.sameValue(explicit3, "12:34:56", "default roundingMode is trunc");
+const implicit3 = time.toString({ smallestUnit: "second" });
+assert.sameValue(implicit3, "12:34:56", "default roundingMode is trunc");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..2d7a86a9c0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-wrong-type.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => time.toString({ smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => assert.sameValue(result, "12:34:56.123987", descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-fractionalseconddigits.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-fractionalseconddigits.js
new file mode 100644
index 0000000000..9a9b87c3d1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-fractionalseconddigits.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: fractionalSecondDigits option is not used with smallestUnit present
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 789, 999, 999);
+const tests = [
+ ["minute", "12:34"],
+ ["second", "12:34:56"],
+ ["millisecond", "12:34:56.789"],
+ ["microsecond", "12:34:56.789999"],
+ ["nanosecond", "12:34:56.789999999"],
+];
+
+for (const [smallestUnit, expected] of tests) {
+ const string = time.toString({
+ smallestUnit,
+ fractionalSecondDigits: 5,
+ });
+ assert.sameValue(string, expected, `smallestUnit: "${smallestUnit}" overrides fractionalSecondDigits`);
+}
+
+assert.throws(RangeError, () => time.toString({
+ smallestUnit: "hour",
+ fractionalSecondDigits: 5,
+}), "hour is an invalid smallestUnit but still overrides fractionalSecondDigits");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..7b31d12b17
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-invalid-string.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+const badValues = [
+ "era",
+ "eraYear",
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => time.toString({ smallestUnit }),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..9fde77c2c4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-plurals-accepted.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 789, 999, 999);
+const validUnits = [
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => time.toString({ smallestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-undefined.js
new file mode 100644
index 0000000000..fb45251a66
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-undefined.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: Fallback value for smallestUnit option
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+
+const explicit1 = time.toString({ smallestUnit: undefined, fractionalSecondDigits: 6 });
+assert.sameValue(explicit1, "12:34:56.123987", "default smallestUnit defers to fractionalSecondDigits");
+const implicit1 = time.toString({ fractionalSecondDigits: 6 });
+assert.sameValue(implicit1, "12:34:56.123987", "default smallestUnit defers to fractionalSecondDigits");
+
+const explicit2 = time.toString({ smallestUnit: undefined, fractionalSecondDigits: 3 });
+assert.sameValue(explicit2, "12:34:56.123", "default smallestUnit defers to fractionalSecondDigits");
+const implicit2 = time.toString({ fractionalSecondDigits: 3 });
+assert.sameValue(implicit2, "12:34:56.123", "default smallestUnit defers to fractionalSecondDigits");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-valid-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-valid-units.js
new file mode 100644
index 0000000000..6d44d85080
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-valid-units.js
@@ -0,0 +1,58 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: Valid units for the smallestUnit option
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 456, 789);
+
+function test(instance, expectations, description) {
+ for (const [smallestUnit, expectedResult] of expectations) {
+ assert.sameValue(instance.toString({ smallestUnit }), expectedResult,
+ `${description} with smallestUnit "${smallestUnit}"`);
+ }
+}
+
+test(
+ time,
+ [
+ ["minute", "12:34"],
+ ["second", "12:34:56"],
+ ["millisecond", "12:34:56.123"],
+ ["microsecond", "12:34:56.123456"],
+ ["nanosecond", "12:34:56.123456789"],
+ ],
+ "subseconds toString"
+);
+
+test(
+ new Temporal.PlainTime(12, 34),
+ [
+ ["minute", "12:34"],
+ ["second", "12:34:00"],
+ ["millisecond", "12:34:00.000"],
+ ["microsecond", "12:34:00.000000"],
+ ["nanosecond", "12:34:00.000000000"],
+ ],
+ "whole minutes toString"
+);
+
+const notValid = [
+ "era",
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+];
+
+notValid.forEach((smallestUnit) => {
+ assert.throws(RangeError, () => time.toString({ smallestUnit }),
+ `"${smallestUnit}" is not a valid unit for the smallestUnit option`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..33715d5b90
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-wrong-type.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tostring
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => time.toString({ smallestUnit }),
+ (result, descr) => assert.sameValue(result, "12:34:56.123987", descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toStringTag/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toStringTag/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toStringTag/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toStringTag/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toStringTag/prop-desc.js
new file mode 100644
index 0000000000..355fee5ac9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toStringTag/prop-desc.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype-@@tostringtag
+description: The @@toStringTag property of Temporal.PlainTime
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype, Symbol.toStringTag, {
+ value: "Temporal.PlainTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toStringTag/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toStringTag/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toStringTag/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..4aab6d8fcd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" });
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..b24fe89cee
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-calendar-datefromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..df8e557c95
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-constructor-in-calendar-fields.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+assert.throws(RangeError, () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..c6b11b1c5e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+ assert.throws(RangeError, () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-leap-second.js
new file mode 100644
index 0000000000..4adfadacb5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-leap-second.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: Leap second is a valid ISO string for PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" });
+assert.sameValue(
+ result1.epochNanoseconds,
+ 1_483_187_696_987_654_321n,
+ "leap second is a valid ISO string for PlainDate"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" });
+assert.sameValue(
+ result2.epochNanoseconds,
+ 1_483_187_696_987_654_321n,
+ "second: 60 is ignored in property bag for PlainDate"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-number.js
new file mode 100644
index 0000000000..a171ac85d7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-number.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }),
+ 'Numbers cannot be used in place of an ISO string for PlainDate'
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-plaindatetime.js
new file mode 100644
index 0000000000..db71724782
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-plaindatetime.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.tozoneddatetime
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainDate by reading internal slots
+info: |
+ sec-temporal.plaintime.prototype.tozoneddatetime step 5:
+ 5. Let _temporalDate_ be ? ToTemporalDate(_temporalDateLike_).
+ sec-temporal-totemporaldate step 2.b:
+ b. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ i. Return ! CreateTemporalDate(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const time = new Temporal.PlainTime(6, 54, 32, 123, 456, 789);
+ const result = time.toZonedDateTime({ plainDate: datetime, timeZone: "UTC" });
+ assert.sameValue(result.year, 2000, "year result");
+ assert.sameValue(result.month, 5, "month result");
+ assert.sameValue(result.day, 2, "day result");
+ assert.sameValue(result.hour, 6, "hour result");
+ assert.sameValue(result.minute, 54, "minute result");
+ assert.sameValue(result.second, 32, "second result");
+ assert.sameValue(result.millisecond, 123, "millisecond result");
+ assert.sameValue(result.microsecond, 456, "microsecond result");
+ assert.sameValue(result.nanosecond, 789, "nanosecond result");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-primitive.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-primitive.js
new file mode 100644
index 0000000000..71011392de
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-primitive.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.tozoneddatetime
+description: TypeError thrown if a primitive is passed as the argument
+info: |
+ Temporal.PlainTime.prototype.toZonedDateTime ( item )
+
+ 3. If Type(item) is not Object, then
+ a. Throw a TypeError exception.
+features: [Symbol, Temporal]
+---*/
+
+const instance = Temporal.PlainTime.from("00:00");
+
+assert.throws(TypeError, () => instance.toZonedDateTime(undefined), "undefined");
+assert.throws(TypeError, () => instance.toZonedDateTime(null), "null");
+assert.throws(TypeError, () => instance.toZonedDateTime(true), "true");
+assert.throws(TypeError, () => instance.toZonedDateTime(""), "empty string");
+assert.throws(TypeError, () => instance.toZonedDateTime(Symbol()), "symbol");
+assert.throws(TypeError, () => instance.toZonedDateTime(1), "1");
+assert.throws(TypeError, () => instance.toZonedDateTime(1n), "1n");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..476177705e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" });
+assert.sameValue(result.epochNanoseconds, 217_168_496_987_654_321n, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..126ebf0d1c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" });
+assert.sameValue(
+ result.epochNanoseconds,
+ 217_168_496_987_654_321n,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..d1ec94a514
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-number.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..11f185579a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-string.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" });
+assert.sameValue(result.epochNanoseconds, 217_168_496_987_654_321n, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..fca5483d19
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..2acf24b30d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-year-zero.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-missing-properties.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-missing-properties.js
new file mode 100644
index 0000000000..e2aaab1a86
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-missing-properties.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.tozoneddatetime
+description: Both plainDate and timeZone properties need to not be undefined.
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+const plainDate = new Temporal.PlainDate(2022, 5, 19);
+const timeZone = new Temporal.TimeZone("UTC");
+assert.throws(TypeError, () => instance.toZonedDateTime({}),
+ "no properties");
+assert.throws(TypeError, () => instance.toZonedDateTime({ plainDate }),
+ "only plainDate");
+assert.throws(TypeError, () => instance.toZonedDateTime({ plainDate, timeZone: undefined }),
+ "timeZone explicitly undefined");
+assert.throws(TypeError, () => instance.toZonedDateTime({ timeZone }),
+ "only timeZone");
+assert.throws(TypeError, () => instance.toZonedDateTime({ plainDate: undefined, timeZone }),
+ "plainDate explicitly undefined");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..ace48b7cb0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-proto-in-calendar-fields.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+assert.throws(RangeError, () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..c48022d265
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-calendar-annotation.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" });
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 957_270_896_987_654_321n,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..dc53b6ebfe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!foo=bar]",
+ "1970-01-01T00:00[!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..fcc2b3f072
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-date-with-utc-offset.js
@@ -0,0 +1,48 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" });
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 957_270_896_987_654_321n,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+const invalidStrings = [
+ "2022-09-15Z",
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }),
+ `"${arg}" UTC offset without time is not valid for PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-invalid.js
new file mode 100644
index 0000000000..1757339404
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-invalid.js
@@ -0,0 +1,64 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }),
+ `"${arg}" should not be a valid ISO string for a PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..d381bc0212
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-multiple-calendar.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..a95338db3d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-multiple-time-zone.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[UTC][UTC]",
+ "1970-01-01T00:00[UTC][UTC]",
+ "1970-01-01T00:00[!UTC][UTC]",
+ "1970-01-01T00:00[UTC][!UTC]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00[UTC][foo=bar][UTC]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-time-separators.js
new file mode 100644
index 0000000000..653debbd30
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-time-separators.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" });
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 957_270_896_987_654_321n,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..c49c5b85d7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-time-zone-annotation.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[Asia/Kolkata]", "named, with no time"],
+ ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"],
+ ["2000-05-02[+00:00]", "numeric, with no time"],
+ ["2000-05-02[!-02:30]", "numeric, with ! and no time"],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" });
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 957_270_896_987_654_321n,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..b570aa57c5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-unknown-annotation.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" });
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 957_270_896_987_654_321n,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..c36daff5df
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-with-utc-designator.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }),
+ "String with UTC designator should not be valid as a PlainDate"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-wrong-type.js
new file mode 100644
index 0000000000..762fa6e9b9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-wrong-type.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [undefined, "undefined"], // plainDate property is required
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-convert.js
new file mode 100644
index 0000000000..43ccf45aec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+assert.throws(Test262Error, () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..a8dea3776c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-slots.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" });
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..39ca85488e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => time.toZonedDateTime({ plainDate: datetime, timeZone: "UTC" }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..79284d6948
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => time.toZonedDateTime({ plainDate: datetime, timeZone: "UTC" }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..359fcb371c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => time.toZonedDateTime({ plainDate: datetime, timeZone: "UTC" }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..b1e0abaa25
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => time.toZonedDateTime({ plainDate: datetime, timeZone: "UTC" }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/basic.js
new file mode 100644
index 0000000000..8dd138b0eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/basic.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.tozoneddatetime
+description: Basic tests for toZonedDateTime().
+features: [Temporal]
+---*/
+
+const plainTime = Temporal.PlainTime.from('12:00');
+const plainDate = Temporal.PlainDate.from('2020-07-08');
+const timeZone = Temporal.TimeZone.from('-07:00');
+
+const objects = plainTime.toZonedDateTime({ timeZone, plainDate });
+assert.sameValue(objects.epochNanoseconds, 1594234800000000000n, "objects: epochNanoseconds");
+assert.sameValue(objects.getTimeZone(), timeZone, "objects: timeZone");
+
+const timeZoneString = plainTime.toZonedDateTime({ timeZone: "-07:00", plainDate });
+assert.sameValue(timeZoneString.epochNanoseconds, 1594234800000000000n, "timeZone string: epochNanoseconds");
+assert.sameValue(timeZoneString.timeZoneId, "-07:00", "timeZone string: timeZone");
+
+const plainDateString = plainTime.toZonedDateTime({ timeZone, plainDate: "2020-07-08" });
+assert.sameValue(plainDateString.epochNanoseconds, 1594234800000000000n, "plainDate string: epochNanoseconds");
+assert.sameValue(plainDateString.getTimeZone(), timeZone, "plainDate string: timeZone");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/branding.js
new file mode 100644
index 0000000000..9df89a0dd4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toZonedDateTime = Temporal.PlainTime.prototype.toZonedDateTime;
+
+assert.sameValue(typeof toZonedDateTime, "function");
+
+const args = [{ plainDate: "2022-05-19", timeZone: "UTC" }];
+
+assert.throws(TypeError, () => toZonedDateTime.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => toZonedDateTime.apply(null, args), "null");
+assert.throws(TypeError, () => toZonedDateTime.apply(true, args), "true");
+assert.throws(TypeError, () => toZonedDateTime.apply("", args), "empty string");
+assert.throws(TypeError, () => toZonedDateTime.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => toZonedDateTime.apply(1, args), "1");
+assert.throws(TypeError, () => toZonedDateTime.apply({}, args), "plain object");
+assert.throws(TypeError, () => toZonedDateTime.apply(Temporal.PlainTime, args), "Temporal.PlainTime");
+assert.throws(TypeError, () => toZonedDateTime.apply(Temporal.PlainTime.prototype, args), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/builtin.js
new file mode 100644
index 0000000000..e9271270ce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: >
+ Tests that Temporal.PlainTime.prototype.toZonedDateTime
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainTime.prototype.toZonedDateTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.prototype.toZonedDateTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.prototype.toZonedDateTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.prototype.toZonedDateTime.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..a94747cf81
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-datefromfields-called-with-options-undefined.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321, calendar);
+instance.toZonedDateTime({ plainDate: { year: 2000, month: 5, day: 3, calendar }, timeZone: new Temporal.TimeZone("UTC") });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-fields-iterable.js
new file mode 100644
index 0000000000..b8c58af599
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-fields-iterable.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaintime.prototype.tozoneddatetime step 5:
+ 3. Let _temporalDate_ be ? ToTemporalDate(_temporalDateLike_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const time = new Temporal.PlainTime(13, 3);
+const calendar = TemporalHelpers.calendarFieldsIterable();
+time.toZonedDateTime({ plainDate: { year: 2000, month: 5, day: 3, calendar }, timeZone: "UTC" });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-temporal-object.js
new file mode 100644
index 0000000000..a9a98f20fd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-temporal-object.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaintime.prototype.tozoneddatetime step 5:
+ 5. Let _temporalDate_ be ? ToTemporalDate(_temporalDateLike_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => {
+ const time = new Temporal.PlainTime(13, 3);
+ const result = time.toZonedDateTime({ timeZone: "UTC", plainDate: { year: 2000, month: 5, day: 3, calendar: temporalObject } });
+ assert.sameValue(result.getCalendar(), calendar, "Temporal object coerced to calendar");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/getpossibleinstantsfor-called-with-iso8601-calendar.js
new file mode 100644
index 0000000000..729c58da0f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/getpossibleinstantsfor-called-with-iso8601-calendar.js
@@ -0,0 +1,58 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: >
+ Time zone's getPossibleInstantsFor is called with a PlainDateTime with the
+ built-in ISO 8601 calendar
+features: [Temporal]
+info: |
+ DisambiguatePossibleInstants:
+ 2. Let _n_ be _possibleInstants_'s length.
+ ...
+ 5. Assert: _n_ = 0.
+ ...
+ 19. If _disambiguation_ is *"earlier"*, then
+ ...
+ c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_).
+ ...
+ 20. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ ...
+ 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_).
+---*/
+
+class SkippedDateTime extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ this.calls = 0;
+ }
+
+ getPossibleInstantsFor(dateTime) {
+ // Calls occur in pairs. For the first one return no possible instants so
+ // that DisambiguatePossibleInstants will call it again
+ if (this.calls++ % 2 == 0) {
+ return [];
+ }
+
+ assert.sameValue(
+ dateTime.getISOFields().calendar,
+ "iso8601",
+ "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar"
+ );
+ return super.getPossibleInstantsFor(dateTime);
+ }
+}
+
+const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601");
+const timeZone = new SkippedDateTime();
+
+const instance = new Temporal.PlainTime(12, 34, 56);
+instance.toZonedDateTime({ timeZone, plainDate: new Temporal.PlainDate(2000, 5, 2, nonBuiltinISOCalendar) });
+
+assert.sameValue(timeZone.calls, 2, "getPossibleInstantsFor should have been called 2 times");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..56340351a6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/infinity-throws-rangeerror.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(15);
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.toZonedDateTime({ timeZone: "UTC", plainDate: { ...base, [prop]: inf } }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.toZonedDateTime({ timeZone: "UTC", plainDate: { ...base, [prop]: obj } }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/length.js
new file mode 100644
index 0000000000..73dda4c229
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: Temporal.PlainTime.prototype.toZonedDateTime.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.toZonedDateTime, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/name.js
new file mode 100644
index 0000000000..c6520773db
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: Temporal.PlainTime.prototype.toZonedDateTime.name is "toZonedDateTime".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.toZonedDateTime, "name", {
+ value: "toZonedDateTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/not-a-constructor.js
new file mode 100644
index 0000000000..5e5068b867
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: >
+ Temporal.PlainTime.prototype.toZonedDateTime does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainTime.prototype.toZonedDateTime();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.prototype.toZonedDateTime), false,
+ "isConstructor(Temporal.PlainTime.prototype.toZonedDateTime)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/order-of-operations.js
new file mode 100644
index 0000000000..168f4cf078
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/order-of-operations.js
@@ -0,0 +1,118 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: User code calls happen in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "get item.plainDate",
+ "get item.plainDate.calendar",
+ "has item.plainDate.calendar.dateAdd",
+ "has item.plainDate.calendar.dateFromFields",
+ "has item.plainDate.calendar.dateUntil",
+ "has item.plainDate.calendar.day",
+ "has item.plainDate.calendar.dayOfWeek",
+ "has item.plainDate.calendar.dayOfYear",
+ "has item.plainDate.calendar.daysInMonth",
+ "has item.plainDate.calendar.daysInWeek",
+ "has item.plainDate.calendar.daysInYear",
+ "has item.plainDate.calendar.fields",
+ "has item.plainDate.calendar.id",
+ "has item.plainDate.calendar.inLeapYear",
+ "has item.plainDate.calendar.mergeFields",
+ "has item.plainDate.calendar.month",
+ "has item.plainDate.calendar.monthCode",
+ "has item.plainDate.calendar.monthDayFromFields",
+ "has item.plainDate.calendar.monthsInYear",
+ "has item.plainDate.calendar.weekOfYear",
+ "has item.plainDate.calendar.year",
+ "has item.plainDate.calendar.yearMonthFromFields",
+ "has item.plainDate.calendar.yearOfWeek",
+ "get item.plainDate.calendar.dateFromFields",
+ "get item.plainDate.calendar.fields",
+ "call item.plainDate.calendar.fields",
+ "get item.plainDate.day",
+ "get item.plainDate.day.valueOf",
+ "call item.plainDate.day.valueOf",
+ "get item.plainDate.month",
+ "get item.plainDate.month.valueOf",
+ "call item.plainDate.month.valueOf",
+ "get item.plainDate.monthCode",
+ "get item.plainDate.monthCode.toString",
+ "call item.plainDate.monthCode.toString",
+ "get item.plainDate.year",
+ "get item.plainDate.year.valueOf",
+ "call item.plainDate.year.valueOf",
+ "call item.plainDate.calendar.dateFromFields",
+ "get item.timeZone",
+ "has item.timeZone.getOffsetNanosecondsFor",
+ "has item.timeZone.getPossibleInstantsFor",
+ "has item.timeZone.id",
+ "get item.timeZone.getOffsetNanosecondsFor",
+ "get item.timeZone.getPossibleInstantsFor",
+ "call item.timeZone.getPossibleInstantsFor",
+];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "item.plainDate.calendar");
+const instance = new Temporal.PlainTime(2, 30);
+
+const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "item.timeZone", {
+ getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor,
+ getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor,
+});
+
+const plainDate = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 1,
+ monthCode: "M01",
+ day: 1,
+ calendar,
+}, "item.plainDate");
+instance.toZonedDateTime(TemporalHelpers.propertyBagObserver(actual, {
+ plainDate,
+ timeZone,
+}, "item"));
+assert.compareArray(actual, expected, "order of operations at normal wall-clock time");
+actual.splice(0); // clear
+
+const fallBackPlainDate = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 10,
+ monthCode: "M10",
+ day: 29,
+ calendar,
+}, "item.plainDate");
+const fallBackInstance = new Temporal.PlainTime(1, 30);
+fallBackInstance.toZonedDateTime(TemporalHelpers.propertyBagObserver(actual, {
+ plainDate: fallBackPlainDate,
+ timeZone,
+}, "item"));
+assert.compareArray(actual, expected, "order of operations at repeated wall-clock time");
+actual.splice(0); // clear
+
+const springForwardPlainDate = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 4,
+ monthCode: "M04",
+ day: 2,
+ calendar,
+}, "item.plainDate");
+instance.toZonedDateTime(TemporalHelpers.propertyBagObserver(actual, {
+ plainDate: springForwardPlainDate,
+ timeZone,
+}, "item"));
+assert.compareArray(actual, expected.concat([
+ "call item.timeZone.getOffsetNanosecondsFor",
+ "call item.timeZone.getOffsetNanosecondsFor",
+ "call item.timeZone.getPossibleInstantsFor",
+]), "order of operations at skipped wall-clock time");
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/plaindate-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/plaindate-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..6df15bc12c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/plaindate-infinity-throws-rangeerror.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(15);
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.toZonedDateTime({ timeZone: "UTC", plainDate: { ...base, [prop]: inf } }), `${prop} property cannot be ${inf} in plainDate`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.toZonedDateTime({ timeZone: "UTC", plainDate: { ...base, [prop]: obj } }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/prop-desc.js
new file mode 100644
index 0000000000..354de10328
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: The "toZonedDateTime" property of Temporal.PlainTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.prototype.toZonedDateTime,
+ "function",
+ "`typeof PlainTime.prototype.toZonedDateTime` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime.prototype, "toZonedDateTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-case-insensitive.js
new file mode 100644
index 0000000000..1c276cdc60
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-case-insensitive.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: Time zone names are case insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+
+const timeZone = 'uTc';
+const result = instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone });
+assert.sameValue(result.timeZoneId, 'UTC', `Time zone created from string "${timeZone}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..ef8f207249
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ const plainDate = new Temporal.PlainDate(2000, 5, 2);
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ assert.throws(RangeError, () => time.toZonedDateTime({ plainDate, timeZone }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..224ce6a5ed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ const plainDate = new Temporal.PlainDate(2000, 5, 2);
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => time.toZonedDateTime({ plainDate, timeZone }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..522e2c7b0b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ const plainDate = new Temporal.PlainDate(2000, 5, 2);
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ assert.throws(RangeError, () => time.toZonedDateTime({ plainDate, timeZone }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..aafa053af7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ const plainDate = new Temporal.PlainDate(2000, 5, 2);
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ assert.throws(TypeError, () => time.toZonedDateTime({ plainDate, timeZone }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 0000000000..78c729ac0f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.plaintime.prototype.tozoneddatetime step 10:
+ 10. Let _instant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _temporalDateTime_, *"compatible"*).
+ sec-temporal-builtintimezonegetinstantfor step 1:
+ 1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-builtintimezonegetinstantfor step 14:
+ 14. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ sec-temporal-builtintimezonegetinstantfor step 16:
+ 16. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _later_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected1 = [
+ "2000-05-02T12:34:56.987654321",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ time.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone });
+}, expected1);
+
+// Same, but test the other path where the time doesn't exist and
+// GetPossibleInstantsFor is called again on a later time
+
+const expected2 = [
+ "2030-01-01T00:30:00",
+ "2030-01-01T01:30:00",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const time = new Temporal.PlainTime(0, 30);
+ time.toZonedDateTime({ plainDate: new Temporal.PlainDate(2030, 1, 1), timeZone });
+}, expected2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-datetime.js
new file mode 100644
index 0000000000..cce8691a13
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-datetime.js
@@ -0,0 +1,65 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone }), "bare date-time string is not a time zone");
+
+[
+ "2021-08-19T17:30-07:00:01",
+ "2021-08-19T17:30-07:00:00",
+ "2021-08-19T17:30-07:00:00.1",
+ "2021-08-19T17:30-07:00:00.0",
+ "2021-08-19T17:30-07:00:00.01",
+ "2021-08-19T17:30-07:00:00.00",
+ "2021-08-19T17:30-07:00:00.001",
+ "2021-08-19T17:30-07:00:00.000",
+ "2021-08-19T17:30-07:00:00.0001",
+ "2021-08-19T17:30-07:00:00.0000",
+ "2021-08-19T17:30-07:00:00.00001",
+ "2021-08-19T17:30-07:00:00.00000",
+ "2021-08-19T17:30-07:00:00.000001",
+ "2021-08-19T17:30-07:00:00.000000",
+ "2021-08-19T17:30-07:00:00.0000001",
+ "2021-08-19T17:30-07:00:00.0000000",
+ "2021-08-19T17:30-07:00:00.00000001",
+ "2021-08-19T17:30-07:00:00.00000000",
+ "2021-08-19T17:30-07:00:00.000000001",
+ "2021-08-19T17:30-07:00:00.000000000",
+].forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone }),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone`
+ );
+});
+
+timeZone = "2021-08-19T17:30Z";
+const result1 = instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone });
+assert.sameValue(result1.timeZoneId, "UTC", "date-time + Z is UTC time zone");
+
+timeZone = "2021-08-19T17:30-07:00";
+const result2 = instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone });
+assert.sameValue(result2.timeZoneId, "-07:00", "date-time + offset is the offset time zone");
+
+timeZone = "2021-08-19T17:30[UTC]";
+const result3 = instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone });
+assert.sameValue(result3.timeZoneId, "UTC", "date-time + IANA annotation is the IANA time zone");
+
+timeZone = "2021-08-19T17:30Z[UTC]";
+const result4 = instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone });
+assert.sameValue(result4.timeZoneId, "UTC", "date-time + Z + IANA annotation is the IANA time zone");
+
+timeZone = "2021-08-19T17:30-07:00[UTC]";
+const result5 = instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone });
+assert.sameValue(result5.timeZoneId, "UTC", "date-time + offset + IANA annotation is the IANA time zone");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-leap-second.js
new file mode 100644
index 0000000000..b9ffeaf896
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-leap-second.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: Leap second is a valid ISO string for TimeZone
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
+
+const result = instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone });
+assert.sameValue(result.timeZoneId, "UTC", "leap second is a valid ISO string for TimeZone");
+
+timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
+assert.throws(RangeError, () => instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone }), "leap second in time zone name not valid");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-multiple-offsets.js
new file mode 100644
index 0000000000..7891dfd8b0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-multiple-offsets.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
+
+const result = instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone });
+assert.sameValue(result.timeZoneId, "+01:46", "Time zone string determined from bracket name");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-year-zero.js
new file mode 100644
index 0000000000..e89a158fab
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-year-zero.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainTime();
+invalidStrings.forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone }),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string.js
new file mode 100644
index 0000000000..1dfee59178
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: Time zone IDs are valid input for a time zone
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainTime();
+
+["UTC", "+01:30"].forEach((timeZone) => {
+ const result = instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone });
+ assert.sameValue(result.getISOFields().timeZone, timeZone, `time zone slot should store string "${timeZone}"`);
+});
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-wrong-type.js
new file mode 100644
index 0000000000..8dbf4e45dd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-wrong-type.js
@@ -0,0 +1,42 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [timeZone, description] of primitiveTests) {
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone }),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "object not implementing time zone protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone }), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/year-zero.js
new file mode 100644
index 0000000000..fd73928276
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/year-zero.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-cast.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-cast.js
new file mode 100644
index 0000000000..8fae64634f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-cast.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Casts the argument
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const plainTime = new Temporal.PlainTime(15, 23, 30, 123, 456, 789);
+TemporalHelpers.assertDuration(plainTime.until("16:34"),
+ 0, 0, 0, 0, /* hours = */ 1, /* minutes = */ 10, /* seconds = */ 29, 876, 543, 211, "string");
+TemporalHelpers.assertDuration(plainTime.until({ hour: 16, minute: 34 }),
+ 0, 0, 0, 0, /* hours = */ 1, /* minutes = */ 10, /* seconds = */ 29, 876, 543, 211, "object");
+
+assert.throws(TypeError, () => plainTime.until({}), "empty");
+assert.throws(TypeError, () => plainTime.until({ minutes: 30 }), "only plural 'minutes'");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-number.js
new file mode 100644
index 0000000000..184cc6609e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-number.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: A number is invalid in place of an ISO string for Temporal.PlainTime
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const numbers = [
+ 1,
+ -123456.987654321,
+ 1234567,
+ 123456.9876543219,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.until(arg),
+ `A number (${arg}) is not a valid ISO string for PlainTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..5131007c25
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-calendar-annotation.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["12:34:56.987654321[u-ca=iso8601]", "without time zone"],
+ ["12:34:56.987654321[UTC][u-ca=iso8601]", "with time zone"],
+ ["12:34:56.987654321[!u-ca=iso8601]", "with ! and no time zone"],
+ ["12:34:56.987654321[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["T12:34:56.987654321[u-ca=iso8601]", "with T and no time zone"],
+ ["T12:34:56.987654321[UTC][u-ca=iso8601]", "with T and time zone"],
+ ["T12:34:56.987654321[!u-ca=iso8601]", "with T, !, and no time zone"],
+ ["T12:34:56.987654321[UTC][!u-ca=iso8601]", "with T, !, and time zone"],
+ ["1970-01-01T12:34:56.987654321[u-ca=iso8601]", "with date and no time zone"],
+ ["1970-01-01T12:34:56.987654321[UTC][u-ca=iso8601]", "with date and time zone"],
+ ["1970-01-01T12:34:56.987654321[!u-ca=iso8601]", "with !, date, and no time zone"],
+ ["1970-01-01T12:34:56.987654321[UTC][!u-ca=iso8601]", "with !, date, and time zone"],
+ ["12:34:56.987654321[u-ca=hebrew]", "calendar annotation ignored"],
+ ["12:34:56.987654321[u-ca=unknown]", "calendar annotation ignored even if unknown calendar"],
+ ["12:34:56.987654321[!u-ca=unknown]", "calendar annotation ignored even if unknown calendar with !"],
+ ["1970-01-01T12:34:56.987654321[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..8f5566b2a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "00:00[!foo=bar]",
+ "T00:00[!foo=bar]",
+ "1970-01-01T00:00[!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..1fbc1405b8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-date-with-utc-offset.js
@@ -0,0 +1,53 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const validStrings = [
+ "12:34:56.987654321+00:00",
+ "12:34:56.987654321+00:00[UTC]",
+ "12:34:56.987654321+00:00[!UTC]",
+ "12:34:56.987654321-02:30[America/St_Johns]",
+ "1976-11-18T12:34:56.987654321+00:00",
+ "1976-11-18T12:34:56.987654321+00:00[UTC]",
+ "1976-11-18T12:34:56.987654321+00:00[!UTC]",
+ "1976-11-18T12:34:56.987654321-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.until(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `"${arg}" is a valid UTC offset with time for PlainTime`
+ );
+}
+
+const invalidStrings = [
+ "2022-09-15Z",
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `"${arg}" UTC offset without time is not valid for PlainTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..60669f4cb5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-multiple-calendar.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..d98c81ecf6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-multiple-time-zone.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "00:00[UTC][UTC]",
+ "T00:00[UTC][UTC]",
+ "1970-01-01T00:00[UTC][UTC]",
+ "1970-01-01T00:00[!UTC][UTC]",
+ "1970-01-01T00:00[UTC][!UTC]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00[UTC][foo=bar][UTC]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-no-implicit-midnight.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-no-implicit-midnight.js
new file mode 100644
index 0000000000..6d0b98e8e5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-no-implicit-midnight.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: RangeError thrown if a date-only string is passed in a PlainTime context
+features: [Temporal, arrow-function]
+---*/
+
+const arg = "2019-10-01";
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ "Date-only string throws, does not implicitly convert to midnight"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-time-designator-required-for-disambiguation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-time-designator-required-for-disambiguation.js
new file mode 100644
index 0000000000..4c73919116
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-time-designator-required-for-disambiguation.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: ISO 8601 time designator "T" required in cases of ambiguity
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+TemporalHelpers.ISO.plainTimeStringsAmbiguous().forEach((string) => {
+ let arg = string;
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `'${arg}' is ambiguous and requires T prefix`
+ );
+ // The same string with a T prefix should not throw:
+ arg = `T${string}`;
+ instance.until(arg);
+
+ arg = ` ${string}`;
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `space is not accepted as a substitute for T prefix: '${arg}'`
+ );
+});
+
+// None of these should throw without a T prefix, because they are unambiguously time strings:
+TemporalHelpers.ISO.plainTimeStringsUnambiguous().forEach(
+ (arg) => instance.until(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-time-separators.js
new file mode 100644
index 0000000000..176b7c51a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-time-separators.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T12:34:56.987654321", "uppercase T"],
+ ["1976-11-18t12:34:56.987654321", "lowercase T"],
+ ["1976-11-18 12:34:56.987654321", "space between date and time"],
+ ["T12:34:56.987654321", "time-only uppercase T"],
+ ["t12:34:56.987654321", "time-only lowercase T"],
+];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..7f01564d22
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-time-zone-annotation.js
@@ -0,0 +1,51 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["12:34:56.987654321[Asia/Kolkata]", "named, with no offset"],
+ ["12:34:56.987654321[!Europe/Vienna]", "named, with ! and no offset"],
+ ["12:34:56.987654321[+00:00]", "numeric, with no offset"],
+ ["12:34:56.987654321[!-02:30]", "numeric, with ! and no offset"],
+ ["T12:34:56.987654321[UTC]", "named, with T and no offset"],
+ ["T12:34:56.987654321[!Africa/Abidjan]", "named, with T, !, and no offset"],
+ ["T12:34:56.987654321[+01:00]", "numeric, with T and no offset"],
+ ["T12:34:56.987654321[!-08:00]", "numeric, with T, !, and no offset"],
+ ["12:34:56.987654321+00:00[America/Sao_Paulo]", "named, with offset"],
+ ["12:34:56.987654321+00:00[!Asia/Tokyo]", "named, with ! and offset"],
+ ["12:34:56.987654321+00:00[-02:30]", "numeric, with offset"],
+ ["12:34:56.987654321+00:00[!+00:00]", "numeric, with ! and offset"],
+ ["T12:34:56.987654321+00:00[America/New_York]", "named, with T and offset"],
+ ["T12:34:56.987654321+00:00[!UTC]", "named, with T, !, and offset"],
+ ["T12:34:56.987654321+00:00[-08:00]", "numeric, with T and offset"],
+ ["T12:34:56.987654321+00:00[!+01:00]", "numeric, with T, !, and offset"],
+ ["1970-01-01T12:34:56.987654321[Africa/Lagos]", "named, with date and no offset"],
+ ["1970-01-01T12:34:56.987654321[!America/Vancouver]", "named, with date, !, and no offset"],
+ ["1970-01-01T12:34:56.987654321[+00:00]", "numeric, with date and no offset"],
+ ["1970-01-01T12:34:56.987654321[!-02:30]", "numeric, with date, !, and no offset"],
+ ["1970-01-01T12:34:56.987654321+00:00[Europe/London]", "named, with date and offset"],
+ ["1970-01-01T12:34:56.987654321+00:00[!Asia/Seoul]", "named, with date, offset, and !"],
+ ["1970-01-01T12:34:56.987654321+00:00[+01:00]", "numeric, with date and offset"],
+ ["1970-01-01T12:34:56.987654321+00:00[!-08:00]", "numeric, with date, offset, and !"],
+];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..7cac3ea843
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-unknown-annotation.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["12:34:56.987654321[foo=bar]", "alone"],
+ ["12:34:56.987654321[UTC][foo=bar]", "with time zone"],
+ ["12:34:56.987654321[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["T12:34:56.987654321[foo=bar]", "with T"],
+ ["T12:34:56.987654321[UTC][foo=bar]", "with T and time zone"],
+ ["T12:34:56.987654321[u-ca=iso8601][foo=bar]", "with T and calendar"],
+ ["T12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with T, time zone, and calendar"],
+ ["1970-01-01T12:34:56.987654321[foo=bar]", "with date"],
+ ["1970-01-01T12:34:56.987654321[UTC][foo=bar]", "with date and time zone"],
+ ["1970-01-01T12:34:56.987654321[u-ca=iso8601][foo=bar]", "with date and calendar"],
+ ["1970-01-01T12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with date, time zone, and calendar"],
+ ["1970-01-01T12:34:56.987654321[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-with-time-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-with-time-designator.js
new file mode 100644
index 0000000000..1e481da34d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-with-time-designator.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: ISO 8601 time designator "T" allowed at the start of PlainTime strings
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.PlainTime(1, 0, 0, 0, 0, 1);
+const validStrings = [
+ "T00:30",
+ "t00:30",
+ "T0030",
+ "t0030",
+ "T00:30:00",
+ "t00:30:00",
+ "T003000",
+ "t003000",
+ "T00:30:00.000000000",
+ "t00:30:00.000000000",
+ "T003000.000000000",
+ "t003000.000000000",
+];
+validStrings.forEach((arg) => {
+ const result = instance.until(arg);
+ TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, -30, 0, 0, 0, -1, `T prefix is accepted: ${arg}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..562616ef60
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-with-utc-designator.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: RangeError thrown if a string with UTC designator is used as a PlainTime
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+ "09:00:00Z[UTC]",
+ "09:00:00Z",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ "String with UTC designator should not be valid as a PlainTime"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-wrong-type.js
new file mode 100644
index 0000000000..b585ea57c8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-wrong-type.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainTime
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => instance.until(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainTime, "Temporal.PlainTime, object"],
+ [Temporal.PlainTime.prototype, "Temporal.PlainTime.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.until(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 0000000000..efb80edab9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,46 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Negative time fields are balanced upwards if the argument is given as ZonedDateTime
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-temporal-totemporaltime step 3.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ ii. 1. Set _plainDateTime_ to ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
+ sec-temporal.plaintime.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalTime(_other_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// This code path is encountered if the time zone offset is negative and its
+// absolute value in nanoseconds is greater than the nanosecond field of the
+// exact time's epoch parts
+const tz = TemporalHelpers.specificOffsetTimeZone(-2);
+const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
+
+const diff = new Temporal.PlainTime().until(datetime);
+
+TemporalHelpers.assertDuration(diff, 0, 0, 0, 0, 1, 1, 1, 1, 0, 999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..f9d9b3ad92
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const instance = new Temporal.PlainTime(15);
+const result = instance.until(datetime);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 1, 50, 35, 0, 0, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..244af3a92d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => time.until(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..14523f2df6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => time.until(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..65a1b4997c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => time.until(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..03a61ee229
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => time.until(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/balance-negative-time-units.js
new file mode 100644
index 0000000000..6dc665fbc0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/balance-negative-time-units.js
@@ -0,0 +1,51 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-differencetime step 8:
+ 8. Let _bt_ be ? BalanceTime(_hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
+ sec-temporal.plaintime.prototype.until step 11:
+ 11. Let _result_ be ! DifferenceTime(_temporalTime_.[[ISOHour]], _temporalTime_.[[ISOMinute]], _temporalTime_.[[ISOSecond]], _temporalTime_.[[ISOMillisecond]], _temporalTime_.[[ISOMicrosecond]], _temporalTime_.[[ISONanosecond]], _other_.[[ISOHour]], _other_.[[ISOMinute]], _other_.[[ISOSecond]], _other_.[[ISOMillisecond]], _other_.[[ISOMicrosecond]], _other_.[[ISONanosecond]]).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(1, 1, 1, 1, 1, 1);
+
+const result1 = new Temporal.PlainTime(0, 0, 0, 0, 0, 2).until(time);
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
+
+const result2 = new Temporal.PlainTime(0, 0, 0, 0, 2).until(time);
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 1, 1, 1, 0, 999, 1, "microseconds balance");
+
+const result3 = new Temporal.PlainTime(0, 0, 0, 2).until(time);
+TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 1, 1, 0, 999, 1, 1, "milliseconds balance");
+
+const result4 = new Temporal.PlainTime(0, 0, 2).until(time);
+TemporalHelpers.assertDuration(result4, 0, 0, 0, 0, 1, 0, 59, 1, 1, 1, "seconds balance");
+
+const result5 = new Temporal.PlainTime(0, 2).until(time);
+TemporalHelpers.assertDuration(result5, 0, 0, 0, 0, 0, 59, 1, 1, 1, 1, "minutes balance");
+
+// This one is different because hours are later balanced again in BalanceDuration
+const result6 = new Temporal.PlainTime(2).until(time);
+TemporalHelpers.assertDuration(result6, 0, 0, 0, 0, 0, -58, -58, -998, -998, -999, "hours balance");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/basic.js
new file mode 100644
index 0000000000..525652c204
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/basic.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Basic usage
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const one = new Temporal.PlainTime(15, 23, 30, 123, 456, 789);
+const two = new Temporal.PlainTime(16, 23, 30, 123, 456, 789);
+const three = new Temporal.PlainTime(17, 0, 30, 123, 456, 789);
+
+TemporalHelpers.assertDuration(one.until(two),
+ 0, 0, 0, 0, /* hours = */ 1, 0, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(two.until(one),
+ 0, 0, 0, 0, /* hours = */ -1, 0, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(one.until(three),
+ 0, 0, 0, 0, /* hours = */ 1, 37, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(three.until(one),
+ 0, 0, 0, 0, /* hours = */ -1, -37, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/branding.js
new file mode 100644
index 0000000000..58e1b3fa75
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const until = Temporal.PlainTime.prototype.until;
+
+assert.sameValue(typeof until, "function");
+
+const args = [new Temporal.PlainTime(12)];
+
+assert.throws(TypeError, () => until.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => until.apply(null, args), "null");
+assert.throws(TypeError, () => until.apply(true, args), "true");
+assert.throws(TypeError, () => until.apply("", args), "empty string");
+assert.throws(TypeError, () => until.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => until.apply(1, args), "1");
+assert.throws(TypeError, () => until.apply({}, args), "plain object");
+assert.throws(TypeError, () => until.apply(Temporal.PlainTime, args), "Temporal.PlainTime");
+assert.throws(TypeError, () => until.apply(Temporal.PlainTime.prototype, args), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/builtin.js
new file mode 100644
index 0000000000..d0e21624e8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: >
+ Tests that Temporal.PlainTime.prototype.until
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainTime.prototype.until),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.prototype.until),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.prototype.until),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.prototype.until.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-invalid-string.js
new file mode 100644
index 0000000000..0284618463
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-invalid-string.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(13, 35, 57, 987, 654, 321);
+const badValues = [
+ "era",
+ "eraYear",
+ "year",
+ "month",
+ "week",
+ "day",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string"
+];
+for (const largestUnit of badValues) {
+ assert.throws(RangeError, () => earlier.until(later, { largestUnit }),
+ `"${largestUnit}" is not a valid value for largestUnit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-plurals-accepted.js
new file mode 100644
index 0000000000..4cd50f0195
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-plurals-accepted.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainTime(13, 35, 57, 988, 655, 322);
+const validUnits = [
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => earlier.until(later, { largestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-smallestunit-mismatch.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-smallestunit-mismatch.js
new file mode 100644
index 0000000000..2dd6528a00
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-smallestunit-mismatch.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: RangeError thrown when smallestUnit is larger than largestUnit
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(13, 35, 57, 987, 654, 321);
+const units = ["hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+for (let largestIdx = 1; largestIdx < units.length; largestIdx++) {
+ for (let smallestIdx = 0; smallestIdx < largestIdx; smallestIdx++) {
+ const largestUnit = units[largestIdx];
+ const smallestUnit = units[smallestIdx];
+ assert.throws(RangeError, () => earlier.until(later, { largestUnit, smallestUnit }));
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-undefined.js
new file mode 100644
index 0000000000..07a593980f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-undefined.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Fallback value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(13, 35, 57, 987, 654, 321);
+
+const explicit = earlier.until(later, { largestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 1, 1, 1, 987, 654, 321, "default largestUnit is hour");
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 1, 1, 1, 987, 654, 321, "default largestUnit is hour");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-wrong-type.js
new file mode 100644
index 0000000000..a685d4cd0a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-wrong-type.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(13, 35, 57, 987, 654, 321);
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "second",
+ (largestUnit) => earlier.until(later, { largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 3661, 987, 654, 321, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit.js
new file mode 100644
index 0000000000..3f84e1be5a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: PlainTime.until with various largestUnit values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+const fourFortyEight = new Temporal.PlainTime(4, 48, 55);
+const elevenFiftyNine = new Temporal.PlainTime(11, 59, 58);
+TemporalHelpers.assertDuration(fourFortyEight.until(elevenFiftyNine), 0, 0, 0, 0, 7, 11, 3, 0, 0, 0, "does not include higher units than necessary (largest unit unspecified)");
+TemporalHelpers.assertDuration(fourFortyEight.until(elevenFiftyNine, { largestUnit: "auto" }), 0, 0, 0, 0, 7, 11, 3, 0, 0, 0, "does not include higher units than necessary (largest unit is auto)");
+TemporalHelpers.assertDuration(fourFortyEight.until(elevenFiftyNine, { largestUnit: "hours" }), 0, 0, 0, 0, 7, 11, 3, 0, 0, 0, "does not include higher units than necessary (largest unit is hours)");
+TemporalHelpers.assertDuration(fourFortyEight.until(elevenFiftyNine, { largestUnit: "minutes" }), 0, 0, 0, 0, 0, 431, 3, 0, 0, 0, "does not include higher units than necessary (largest unit is minutes)");
+TemporalHelpers.assertDuration(fourFortyEight.until(elevenFiftyNine, { largestUnit: "seconds" }), 0, 0, 0, 0, 0, 0, 25863, 0, 0, 0, "does not include higher units than necessary (largest unit is seconds)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/leap-second.js
new file mode 100644
index 0000000000..a3397d4d39
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/leap-second.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Leap second is a valid ISO string for PlainTime
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(23, 59, 59);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.until(arg);
+TemporalHelpers.assertDuration(
+ result1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for PlainTime"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.until(arg);
+TemporalHelpers.assertDuration(
+ result2,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "second: 60 is ignored in property bag for PlainTime"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/length.js
new file mode 100644
index 0000000000..ebb5736dfa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Temporal.PlainTime.prototype.until.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.until, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/name.js
new file mode 100644
index 0000000000..603d27dcac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Temporal.PlainTime.prototype.until.name is "until".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.until, "name", {
+ value: "until",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/not-a-constructor.js
new file mode 100644
index 0000000000..5b4e7dcc2f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: >
+ Temporal.PlainTime.prototype.until does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainTime.prototype.until();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.prototype.until), false,
+ "isConstructor(Temporal.PlainTime.prototype.until)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/options-invalid.js
new file mode 100644
index 0000000000..2340eefee9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/options-invalid.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: TypeError thrown when a primitive is passed as the options argument
+features: [Temporal]
+---*/
+
+const values = [null, true, "hello", Symbol("foo"), 1, 1n];
+const time = new Temporal.PlainTime(15, 23, 30, 123, 456, 789);
+const one = new Temporal.PlainTime(16, 23, 30, 123, 456, 789);
+
+for (const badOptions of values) {
+ assert.throws(TypeError, () => time.until(one, badOptions));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/options-object.js
new file mode 100644
index 0000000000..25cc69bca1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/options-object.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+
+const result1 = instance.until(new Temporal.PlainTime(12, 34, 56), {});
+TemporalHelpers.assertDuration(
+ result1, 0, 0, 0, 0, 12, 34, 56, 0, 0, 0,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.until(new Temporal.PlainTime(12, 34, 56), () => {});
+TemporalHelpers.assertDuration(
+ result2, 0, 0, 0, 0, 12, 34, 56, 0, 0, 0,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/options-undefined.js
new file mode 100644
index 0000000000..c7f622e77a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/options-undefined.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainTime(18, 34, 56, 987, 654, 322);
+
+const explicit = earlier.until(later, undefined);
+assert.sameValue(explicit.hours, 6, "default largest unit is hours");
+assert.sameValue(explicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding");
+
+const implicit = earlier.until(later);
+assert.sameValue(implicit.hours, 6, "default largest unit is hours");
+assert.sameValue(implicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/options-wrong-type.js
new file mode 100644
index 0000000000..6706e49ba3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/options-wrong-type.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainTime();
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.until(new Temporal.PlainTime(12, 34, 56), value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/order-of-operations.js
new file mode 100644
index 0000000000..35a10fcd59
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/order-of-operations.js
@@ -0,0 +1,95 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Properties on an object passed to until() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalTime
+ "get other.hour",
+ "get other.hour.valueOf",
+ "call other.hour.valueOf",
+ "get other.microsecond",
+ "get other.microsecond.valueOf",
+ "call other.microsecond.valueOf",
+ "get other.millisecond",
+ "get other.millisecond.valueOf",
+ "call other.millisecond.valueOf",
+ "get other.minute",
+ "get other.minute.valueOf",
+ "call other.minute.valueOf",
+ "get other.nanosecond",
+ "get other.nanosecond.valueOf",
+ "call other.nanosecond.valueOf",
+ "get other.second",
+ "get other.second.valueOf",
+ "call other.second.valueOf",
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.roundingIncrement",
+ "get options.roundingIncrement",
+ "getOwnPropertyDescriptor options.roundingMode",
+ "get options.roundingMode",
+ "getOwnPropertyDescriptor options.largestUnit",
+ "get options.largestUnit",
+ "getOwnPropertyDescriptor options.smallestUnit",
+ "get options.smallestUnit",
+ "getOwnPropertyDescriptor options.additional",
+ "get options.additional",
+ // GetDifferenceSettings
+ "get options.largestUnit.toString",
+ "call options.largestUnit.toString",
+ "get options.roundingIncrement.valueOf",
+ "call options.roundingIncrement.valueOf",
+ "get options.roundingMode.toString",
+ "call options.roundingMode.toString",
+ "get options.smallestUnit.toString",
+ "call options.smallestUnit.toString",
+];
+const actual = [];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const other = TemporalHelpers.propertyBagObserver(actual, {
+ hour: 1.7,
+ minute: 1.7,
+ second: 1.7,
+ millisecond: 1.7,
+ microsecond: 1.7,
+ nanosecond: 1.7,
+ calendar: "iso8601",
+}, "other");
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ roundingIncrement: 1,
+ roundingMode: "trunc",
+ largestUnit: "hours",
+ smallestUnit: "nanoseconds",
+ additional: true,
+}, "options");
+
+const result = instance.until(other, options);
+assert.compareArray(actual, expected, "order of operations");
+
+actual.splice(0); // clear
+
+// short-circuit does not skip reading options
+const identicalPropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ hour: 12,
+ minute: 34,
+ second: 56,
+ millisecond: 987,
+ microsecond: 654,
+ nanosecond: 321,
+}, "other");
+instance.until(identicalPropertyBag, options);
+assert.compareArray(actual, expected, "order of operations with identical times");
+
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/plaintime-propertybag-no-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/plaintime-propertybag-no-time-units.js
new file mode 100644
index 0000000000..2197e3a558
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/plaintime-propertybag-no-time-units.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Missing time units in property bag default to 0
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(1, 0, 0, 0, 0, 1);
+
+const props = {};
+assert.throws(TypeError, () => instance.until(props), "TypeError if no properties are present");
+
+props.minute = 30;
+const result = instance.until(props);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, -30, 0, 0, 0, -1, "missing time units default to 0");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/prop-desc.js
new file mode 100644
index 0000000000..6c8038cdcd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: The "until" property of Temporal.PlainTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.prototype.until,
+ "function",
+ "`typeof PlainTime.prototype.until` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime.prototype, "until", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/result-sub-second.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/result-sub-second.js
new file mode 100644
index 0000000000..52fdbde50f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/result-sub-second.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Supports sub-second precision
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const time1 = Temporal.PlainTime.from("10:23:15");
+const time2 = Temporal.PlainTime.from("17:15:57.250250250");
+
+TemporalHelpers.assertDuration(time1.until(time2, { largestUnit: "milliseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, /* milliseconds = */ 24762250, 250, 250, "milliseconds");
+
+TemporalHelpers.assertDuration(time1.until(time2, { largestUnit: "microseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, /* milliseconds = */ 0, 24762250250, 250, "microseconds");
+
+TemporalHelpers.assertDuration(time1.until(time2, { largestUnit: "nanoseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, /* milliseconds = */ 0, 0, 24762250250250, "nanoseconds");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/round-cross-unit-boundary.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/round-cross-unit-boundary.js
new file mode 100644
index 0000000000..faef88b1dc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/round-cross-unit-boundary.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Rounding can cross unit boundaries up to largestUnit
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime();
+const later = new Temporal.PlainTime(1, 59, 59);
+const duration = earlier.until(later, { largestUnit: "hours", smallestUnit: "minutes", roundingMode: "expand" });
+TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, "1:60 balances to 2 hours");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-hours.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-hours.js
new file mode 100644
index 0000000000..01ed2f19bd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-hours.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(3, 12, 34, 123, 456, 789);
+const later = new Temporal.PlainTime(13, 47, 57, 988, 655, 322);
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "hours", roundingIncrement: 1 }),
+ 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "hours", roundingIncrement: 2 }),
+ 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "hours", roundingIncrement: 3 }),
+ 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "hours", roundingIncrement: 4 }),
+ 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "hours", roundingIncrement: 6 }),
+ 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "hours", roundingIncrement: 8 }),
+ 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "hours", roundingIncrement: 12 }),
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "hours");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-invalid.js
new file mode 100644
index 0000000000..4037f566ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-invalid.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Tests roundingIncrement restrictions.
+features: [Temporal]
+---*/
+
+const earlier = Temporal.PlainTime.from("08:22:36.123456789");
+const later = Temporal.PlainTime.from("12:39:40.987654321");
+
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "hours", roundingIncrement: 11 }));
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "minutes", roundingIncrement: 29 }));
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "seconds", roundingIncrement: 29 }));
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 29 }));
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 29 }));
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 29 }));
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "hours", roundingIncrement: 24 }));
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "minutes", roundingIncrement: 60 }));
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "seconds", roundingIncrement: 60 }));
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 1000 }));
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 1000 }));
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 1000 }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-microseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-microseconds.js
new file mode 100644
index 0000000000..3ae7e1aea5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-microseconds.js
@@ -0,0 +1,61 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(3, 12, 34, 123, 456, 789);
+const later = new Temporal.PlainTime(13, 47, 57, 988, 655, 322);
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 1 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 2 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 4 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 196, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 5 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 195, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 8 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 192, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 10 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 190, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 20 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 180, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 25 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 175, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 40 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 160, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 50 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 150, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 100 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 100, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 125 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 125, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 200 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 0, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 250 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 0, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 500 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 0, 0, "microseconds");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-milliseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-milliseconds.js
new file mode 100644
index 0000000000..d24a0b8acf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-milliseconds.js
@@ -0,0 +1,61 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(3, 12, 34, 123, 456, 789);
+const later = new Temporal.PlainTime(13, 47, 57, 988, 655, 322);
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 1 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 2 }),
+ 0, 0, 0, 0, 10, 35, 23, 864, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 4 }),
+ 0, 0, 0, 0, 10, 35, 23, 864, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 5 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 8 }),
+ 0, 0, 0, 0, 10, 35, 23, 864, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 10 }),
+ 0, 0, 0, 0, 10, 35, 23, 860, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 20 }),
+ 0, 0, 0, 0, 10, 35, 23, 860, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 25 }),
+ 0, 0, 0, 0, 10, 35, 23, 850, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 40 }),
+ 0, 0, 0, 0, 10, 35, 23, 840, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 50 }),
+ 0, 0, 0, 0, 10, 35, 23, 850, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 100 }),
+ 0, 0, 0, 0, 10, 35, 23, 800, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 125 }),
+ 0, 0, 0, 0, 10, 35, 23, 750, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 200 }),
+ 0, 0, 0, 0, 10, 35, 23, 800, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 250 }),
+ 0, 0, 0, 0, 10, 35, 23, 750, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 500 }),
+ 0, 0, 0, 0, 10, 35, 23, 500, 0, 0, "milliseconds");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-minutes.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-minutes.js
new file mode 100644
index 0000000000..5f98541718
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-minutes.js
@@ -0,0 +1,49 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(3, 12, 34, 123, 456, 789);
+const later = new Temporal.PlainTime(13, 47, 57, 988, 655, 322);
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "minutes", roundingIncrement: 1 }),
+ 0, 0, 0, 0, 10, 35, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "minutes", roundingIncrement: 2 }),
+ 0, 0, 0, 0, 10, 34, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "minutes", roundingIncrement: 3 }),
+ 0, 0, 0, 0, 10, 33, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "minutes", roundingIncrement: 4 }),
+ 0, 0, 0, 0, 10, 32, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "minutes", roundingIncrement: 5 }),
+ 0, 0, 0, 0, 10, 35, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "minutes", roundingIncrement: 6 }),
+ 0, 0, 0, 0, 10, 30, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "minutes", roundingIncrement: 10 }),
+ 0, 0, 0, 0, 10, 30, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "minutes", roundingIncrement: 12 }),
+ 0, 0, 0, 0, 10, 24, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "minutes", roundingIncrement: 15 }),
+ 0, 0, 0, 0, 10, 30, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "minutes", roundingIncrement: 20 }),
+ 0, 0, 0, 0, 10, 20, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "minutes", roundingIncrement: 30 }),
+ 0, 0, 0, 0, 10, 30, 0, 0, 0, 0, "minutes");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-nan.js
new file mode 100644
index 0000000000..5da9205585
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-nan.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: RangeError thrown when roundingIncrement option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaintime.prototype.until step 10:
+ 10. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainTime(13, 35, 57, 988, 655, 322);
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-nanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-nanoseconds.js
new file mode 100644
index 0000000000..59c55f6b84
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-nanoseconds.js
@@ -0,0 +1,61 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(3, 12, 34, 123, 456, 789);
+const later = new Temporal.PlainTime(13, 47, 57, 988, 655, 322);
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 1 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 533, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 2 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 532, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 4 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 532, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 5 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 530, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 8 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 528, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 10 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 530, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 20 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 520, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 25 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 525, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 40 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 520, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 50 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 500, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 100 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 500, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 125 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 500, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 200 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 400, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 250 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 500, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 500 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 500, "nanoseconds");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-non-integer.js
new file mode 100644
index 0000000000..413aa6a02c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-non-integer.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Rounding for roundingIncrement option
+info: |
+ ToTemporalRoundingIncrement ( _normalizedOptions_ )
+
+ 1. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, *"number"*, *undefined*, *1*<sub>𝔽</sub>).
+ 2. If _increment_ is not finite, throw a *RangeError* exception.
+ 3. Let _integerIncrement_ be truncate(ℝ(_increment_)).
+ 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception.
+ 5. Return _integerIncrement_.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(12, 34, 56, 0, 0, 5);
+const result = earlier.until(later, { roundingIncrement: 2.5, roundingMode: "trunc" });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, "roundingIncrement 2.5 truncates to 2");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-out-of-range.js
new file mode 100644
index 0000000000..1c901e5176
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-out-of-range.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: RangeError thrown when roundingIncrement option out of range
+info: |
+ ToTemporalRoundingIncrement ( _normalizedOptions_ )
+
+ 1. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, *"number"*, *undefined*, *1*<sub>𝔽</sub>).
+ 2. If _increment_ is not finite, throw a *RangeError* exception.
+ 3. Let _integerIncrement_ be truncate(ℝ(_increment_)).
+ 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception.
+ 5. Return _integerIncrement_.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(12, 34, 56, 0, 0, 5);
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -1 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 0 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 0.9 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 1e9 + 1 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: Infinity }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-seconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-seconds.js
new file mode 100644
index 0000000000..2a26e524cb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-seconds.js
@@ -0,0 +1,49 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(3, 12, 34, 123, 456, 789);
+const later = new Temporal.PlainTime(13, 47, 57, 988, 655, 322);
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "seconds", roundingIncrement: 1 }),
+ 0, 0, 0, 0, 10, 35, 23, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "seconds", roundingIncrement: 2 }),
+ 0, 0, 0, 0, 10, 35, 22, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "seconds", roundingIncrement: 3 }),
+ 0, 0, 0, 0, 10, 35, 21, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "seconds", roundingIncrement: 4 }),
+ 0, 0, 0, 0, 10, 35, 20, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "seconds", roundingIncrement: 5 }),
+ 0, 0, 0, 0, 10, 35, 20, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "seconds", roundingIncrement: 6 }),
+ 0, 0, 0, 0, 10, 35, 18, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "seconds", roundingIncrement: 10 }),
+ 0, 0, 0, 0, 10, 35, 20, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "seconds", roundingIncrement: 12 }),
+ 0, 0, 0, 0, 10, 35, 12, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "seconds", roundingIncrement: 15 }),
+ 0, 0, 0, 0, 10, 35, 15, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "seconds", roundingIncrement: 20 }),
+ 0, 0, 0, 0, 10, 35, 20, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "seconds", roundingIncrement: 30 }),
+ 0, 0, 0, 0, 10, 35, 0, 0, 0, 0, "seconds");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-undefined.js
new file mode 100644
index 0000000000..46a39fa264
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-undefined.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Fallback value for roundingIncrement option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaintime.prototype.until step 10:
+ 10. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainTime(13, 35, 57, 988, 655, 322);
+
+const explicit = earlier.until(later, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, "default roundingIncrement is 1");
+
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, "default roundingIncrement is 1");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-wrong-type.js
new file mode 100644
index 0000000000..d247679031
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-wrong-type.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Type conversions for roundingIncrement option
+info: |
+ sec-getoption step 8.a:
+ a. Set _value_ to ? ToNumber(value).
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaintime.prototype.until step 10:
+ 10. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainTime(13, 35, 57, 988, 655, 322);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => earlier.until(later, { roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-ceil.js
new file mode 100644
index 0000000000..df3a7ee979
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-ceil.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Tests calculations with roundingMode "ceil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainTime(12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 5], [0, 0, 0, 0, -4]],
+ ["minutes", [0, 0, 0, 0, 4, 18], [0, 0, 0, 0, -4, -17]],
+ ["seconds", [0, 0, 0, 0, 4, 17, 5], [0, 0, 0, 0, -4, -17, -4]],
+ ["milliseconds", [0, 0, 0, 0, 4, 17, 4, 865], [0, 0, 0, 0, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 4, 17, 4, 864, 198], [0, 0, 0, 0, -4, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197, 500], [0, 0, 0, 0, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "ceil";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-expand.js
new file mode 100644
index 0000000000..705de9a868
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-expand.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Tests calculations with roundingMode "expand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainTime(12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 5], [0, 0, 0, 0, -5]],
+ ["minutes", [0, 0, 0, 0, 4, 18], [0, 0, 0, 0, -4, -18]],
+ ["seconds", [0, 0, 0, 0, 4, 17, 5], [0, 0, 0, 0, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 4, 17, 4, 865], [0, 0, 0, 0, -4, -17, -4, -865]],
+ ["microseconds", [0, 0, 0, 0, 4, 17, 4, 864, 198], [0, 0, 0, 0, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197, 500], [0, 0, 0, 0, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "expand";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-floor.js
new file mode 100644
index 0000000000..11270082d7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-floor.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Tests calculations with roundingMode "floor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainTime(12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 4], [0, 0, 0, 0, -5]],
+ ["minutes", [0, 0, 0, 0, 4, 17], [0, 0, 0, 0, -4, -18]],
+ ["seconds", [0, 0, 0, 0, 4, 17, 4], [0, 0, 0, 0, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 4, 17, 4, 864], [0, 0, 0, 0, -4, -17, -4, -865]],
+ ["microseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197], [0, 0, 0, 0, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197, 500], [0, 0, 0, 0, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "floor";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..d987d5c4a4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfCeil.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Tests calculations with roundingMode "halfCeil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainTime(12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 4], [0, 0, 0, 0, -4]],
+ ["minutes", [0, 0, 0, 0, 4, 17], [0, 0, 0, 0, -4, -17]],
+ ["seconds", [0, 0, 0, 0, 4, 17, 5], [0, 0, 0, 0, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 4, 17, 4, 864], [0, 0, 0, 0, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 4, 17, 4, 864, 198], [0, 0, 0, 0, -4, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197, 500], [0, 0, 0, 0, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "halfCeil";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfEven.js
new file mode 100644
index 0000000000..239e861107
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfEven.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Tests calculations with roundingMode "halfEven".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainTime(12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 4], [0, 0, 0, 0, -4]],
+ ["minutes", [0, 0, 0, 0, 4, 17], [0, 0, 0, 0, -4, -17]],
+ ["seconds", [0, 0, 0, 0, 4, 17, 5], [0, 0, 0, 0, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 4, 17, 4, 864], [0, 0, 0, 0, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 4, 17, 4, 864, 198], [0, 0, 0, 0, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197, 500], [0, 0, 0, 0, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "halfEven";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..a8e5f26aef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfExpand.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Tests calculations with roundingMode "halfExpand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainTime(12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 4], [0, 0, 0, 0, -4]],
+ ["minutes", [0, 0, 0, 0, 4, 17], [0, 0, 0, 0, -4, -17]],
+ ["seconds", [0, 0, 0, 0, 4, 17, 5], [0, 0, 0, 0, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 4, 17, 4, 864], [0, 0, 0, 0, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 4, 17, 4, 864, 198], [0, 0, 0, 0, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197, 500], [0, 0, 0, 0, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "halfExpand";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..0908a4bd68
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfFloor.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Tests calculations with roundingMode "halfFloor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainTime(12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 4], [0, 0, 0, 0, -4]],
+ ["minutes", [0, 0, 0, 0, 4, 17], [0, 0, 0, 0, -4, -17]],
+ ["seconds", [0, 0, 0, 0, 4, 17, 5], [0, 0, 0, 0, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 4, 17, 4, 864], [0, 0, 0, 0, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197], [0, 0, 0, 0, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197, 500], [0, 0, 0, 0, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "halfFloor";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..e5bae72f9d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfTrunc.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Tests calculations with roundingMode "halfTrunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainTime(12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 4], [0, 0, 0, 0, -4]],
+ ["minutes", [0, 0, 0, 0, 4, 17], [0, 0, 0, 0, -4, -17]],
+ ["seconds", [0, 0, 0, 0, 4, 17, 5], [0, 0, 0, 0, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 4, 17, 4, 864], [0, 0, 0, 0, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197], [0, 0, 0, 0, -4, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197, 500], [0, 0, 0, 0, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "halfTrunc";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..76598d4a3d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-invalid-string.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(13, 35, 57, 123, 987, 500);
+for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) {
+ assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "microsecond", roundingMode }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-trunc.js
new file mode 100644
index 0000000000..a1f609d8d1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-trunc.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Tests calculations with roundingMode "trunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainTime(12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 4], [0, 0, 0, 0, -4]],
+ ["minutes", [0, 0, 0, 0, 4, 17], [0, 0, 0, 0, -4, -17]],
+ ["seconds", [0, 0, 0, 0, 4, 17, 4], [0, 0, 0, 0, -4, -17, -4]],
+ ["milliseconds", [0, 0, 0, 0, 4, 17, 4, 864], [0, 0, 0, 0, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197], [0, 0, 0, 0, -4, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197, 500], [0, 0, 0, 0, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "trunc";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-undefined.js
new file mode 100644
index 0000000000..c6cef9687a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-undefined.js
@@ -0,0 +1,93 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = Temporal.PlainTime.from("08:22:36.123456789");
+const later = Temporal.PlainTime.from("12:39:40.987654321");
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "hours", roundingMode: undefined }),
+ 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "hours" }),
+ 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit: "hours", roundingMode: undefined }),
+ 0, 0, 0, 0, -4, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit: "hours" }),
+ 0, 0, 0, 0, -4, 0, 0, 0, 0, 0, "hours");
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "minutes", roundingMode: undefined }),
+ 0, 0, 0, 0, 4, 17, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "minutes" }),
+ 0, 0, 0, 0, 4, 17, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit: "minutes", roundingMode: undefined }),
+ 0, 0, 0, 0, -4, -17, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit: "minutes" }),
+ 0, 0, 0, 0, -4, -17, 0, 0, 0, 0, "minutes");
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "seconds", roundingMode: undefined }),
+ 0, 0, 0, 0, 4, 17, 4, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "seconds" }),
+ 0, 0, 0, 0, 4, 17, 4, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit: "seconds", roundingMode: undefined }),
+ 0, 0, 0, 0, -4, -17, -4, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit: "seconds" }),
+ 0, 0, 0, 0, -4, -17, -4, 0, 0, 0, "seconds");
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingMode: undefined }),
+ 0, 0, 0, 0, 4, 17, 4, 864, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds" }),
+ 0, 0, 0, 0, 4, 17, 4, 864, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit: "milliseconds", roundingMode: undefined }),
+ 0, 0, 0, 0, -4, -17, -4, -864, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit: "milliseconds" }),
+ 0, 0, 0, 0, -4, -17, -4, -864, 0, 0, "milliseconds");
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingMode: undefined }),
+ 0, 0, 0, 0, 4, 17, 4, 864, 197, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds" }),
+ 0, 0, 0, 0, 4, 17, 4, 864, 197, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit: "microseconds", roundingMode: undefined }),
+ 0, 0, 0, 0, -4, -17, -4, -864, -197, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit: "microseconds" }),
+ 0, 0, 0, 0, -4, -17, -4, -864, -197, 0, "microseconds");
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingMode: undefined }),
+ 0, 0, 0, 0, 4, 17, 4, 864, 197, 532, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds" }),
+ 0, 0, 0, 0, 4, 17, 4, 864, 197, 532, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit: "nanoseconds", roundingMode: undefined }),
+ 0, 0, 0, 0, -4, -17, -4, -864, -197, -532, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit: "nanoseconds" }),
+ 0, 0, 0, 0, -4, -17, -4, -864, -197, -532, "nanoseconds");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..80fe5287ae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-wrong-type.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(13, 35, 57, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => earlier.until(later, { smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 1, 1, 1, 123, 987, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..64101bd085
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/smallestunit-invalid-string.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(13, 35, 57, 987, 654, 321);
+const badValues = [
+ "era",
+ "eraYear",
+ "year",
+ "month",
+ "week",
+ "day",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => earlier.until(later, { smallestUnit }),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..7fa86c19b3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/smallestunit-plurals-accepted.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainTime(13, 35, 57, 988, 655, 322);
+const validUnits = [
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => earlier.until(later, { smallestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/smallestunit-undefined.js
new file mode 100644
index 0000000000..e32c3a81ea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/smallestunit-undefined.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(13, 35, 57, 987, 654, 321);
+
+const explicit = earlier.until(later, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 1, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 1, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
+const lambda = earlier.until(later, () => {});
+TemporalHelpers.assertDuration(lambda, 0, 0, 0, 0, 1, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..a840df2f93
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/smallestunit-wrong-type.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(13, 35, 57, 987, 654, 321);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => earlier.until(later, { smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 1, 1, 1, 987, 654, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/year-zero.js
new file mode 100644
index 0000000000..2cd3a81649
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/year-zero.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-12-07T03:24:30",
+ "-000000-12-07T03:24:30+01:00",
+ "-000000-12-07T03:24:30+00:00[UTC]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/basic.js
new file mode 100644
index 0000000000..1b9dc24a7d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/basic.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.valueof
+description: Basic tests for valueOf().
+features: [Temporal]
+---*/
+
+const plainTime = Temporal.PlainTime.from("09:36:29.123456789");
+const plainTime2 = Temporal.PlainTime.from("09:36:29.123456789");
+
+assert.throws(TypeError, () => plainTime.valueOf(), "valueOf");
+assert.throws(TypeError, () => plainTime < plainTime, "<");
+assert.throws(TypeError, () => plainTime <= plainTime, "<=");
+assert.throws(TypeError, () => plainTime > plainTime, ">");
+assert.throws(TypeError, () => plainTime >= plainTime, ">=");
+assert.sameValue(plainTime === plainTime, true, "===");
+assert.sameValue(plainTime === plainTime2, false, "===");
+assert.sameValue(plainTime !== plainTime, false, "!==");
+assert.sameValue(plainTime !== plainTime2, true, "!==");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/branding.js
new file mode 100644
index 0000000000..c877532a8a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.valueof
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const valueOf = Temporal.PlainTime.prototype.valueOf;
+
+assert.sameValue(typeof valueOf, "function");
+
+assert.throws(TypeError, () => valueOf.call(undefined), "undefined");
+assert.throws(TypeError, () => valueOf.call(null), "null");
+assert.throws(TypeError, () => valueOf.call(true), "true");
+assert.throws(TypeError, () => valueOf.call(""), "empty string");
+assert.throws(TypeError, () => valueOf.call(Symbol()), "symbol");
+assert.throws(TypeError, () => valueOf.call(1), "1");
+assert.throws(TypeError, () => valueOf.call({}), "plain object");
+assert.throws(TypeError, () => valueOf.call(Temporal.PlainTime), "Temporal.PlainTime");
+assert.throws(TypeError, () => valueOf.call(Temporal.PlainTime.prototype), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/builtin.js
new file mode 100644
index 0000000000..b9cf15bcd4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.valueof
+description: >
+ Tests that Temporal.PlainTime.prototype.valueOf
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainTime.prototype.valueOf),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.prototype.valueOf),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.prototype.valueOf),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.prototype.valueOf.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/length.js
new file mode 100644
index 0000000000..d3d22c93cd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.valueof
+description: Temporal.PlainTime.prototype.valueOf.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.valueOf, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/name.js
new file mode 100644
index 0000000000..33264a7a6f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.valueof
+description: Temporal.PlainTime.prototype.valueOf.name is "valueOf".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.valueOf, "name", {
+ value: "valueOf",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/not-a-constructor.js
new file mode 100644
index 0000000000..46477ced1d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.valueof
+description: >
+ Temporal.PlainTime.prototype.valueOf does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainTime.prototype.valueOf();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.prototype.valueOf), false,
+ "isConstructor(Temporal.PlainTime.prototype.valueOf)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/prop-desc.js
new file mode 100644
index 0000000000..f1473ac5a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.valueof
+description: The "valueOf" property of Temporal.PlainTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.prototype.valueOf,
+ "function",
+ "`typeof PlainTime.prototype.valueOf` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime.prototype, "valueOf", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/argument-not-object.js
new file mode 100644
index 0000000000..c9eddc8b08
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/argument-not-object.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.with
+description: TypeError is thrown if a primitive is passed, including ISO strings
+info: |
+ Temporal.PlainTime.prototype.with ( temporalTimeLike [ , options ] )
+
+ 3. If Type(temporalTimeLike) is not Object, then
+ a. Throw a TypeError exception.
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+assert.throws(TypeError, () => instance.with(undefined), "undefined");
+assert.throws(TypeError, () => instance.with(null), "null");
+assert.throws(TypeError, () => instance.with(true), "true");
+assert.throws(TypeError, () => instance.with(Symbol()), "symbol");
+assert.throws(TypeError, () => instance.with(1), "1");
+assert.throws(TypeError, () => instance.with(1n), "1n");
+
+const strings = [
+ "",
+ "18:05:42.577",
+ "2019-05-17T18:05:42.577",
+ "2019-05-17T18:05:42.577Z",
+ "2019-05-17",
+ "42",
+];
+for (const s of strings) {
+ assert.throws(TypeError, () => instance.with(s), s);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/basic.js
new file mode 100644
index 0000000000..2fa17fda70
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/basic.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.with
+description: Basic tests for with().
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(15, 23, 30, 123, 456, 789);
+TemporalHelpers.assertPlainTime(plainTime, 15, 23, 30, 123, 456, 789, "initial");
+
+const hour = plainTime.with({ hour: 3 });
+TemporalHelpers.assertPlainTime(hour, 3, 23, 30, 123, 456, 789, "hour");
+
+const minute = plainTime.with({ minute: 3 });
+TemporalHelpers.assertPlainTime(minute, 15, 3, 30, 123, 456, 789, "minute");
+
+const second = plainTime.with({ second: 3 });
+TemporalHelpers.assertPlainTime(second, 15, 23, 3, 123, 456, 789, "second");
+
+const millisecond = plainTime.with({ millisecond: 3 });
+TemporalHelpers.assertPlainTime(millisecond, 15, 23, 30, 3, 456, 789, "millisecond");
+
+const microsecond = plainTime.with({ microsecond: 3 });
+TemporalHelpers.assertPlainTime(microsecond, 15, 23, 30, 123, 3, 789, "microsecond");
+
+const nanosecond = plainTime.with({ nanosecond: 3 });
+TemporalHelpers.assertPlainTime(nanosecond, 15, 23, 30, 123, 456, 3, "nanosecond");
+
+const combined = plainTime.with({ minute: 8, nanosecond: 3 });
+TemporalHelpers.assertPlainTime(combined, 15, 8, 30, 123, 456, 3, "combined");
+
+const plural = plainTime.with({ minutes: 8, nanosecond: 3 });
+TemporalHelpers.assertPlainTime(plural, 15, 23, 30, 123, 456, 3, "plural");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/branding.js
new file mode 100644
index 0000000000..f2d4368c5e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.with
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const with_ = Temporal.PlainTime.prototype.with;
+
+assert.sameValue(typeof with_, "function");
+
+const args = [{ hour: 7 }];
+
+assert.throws(TypeError, () => with_.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => with_.apply(null, args), "null");
+assert.throws(TypeError, () => with_.apply(true, args), "true");
+assert.throws(TypeError, () => with_.apply("", args), "empty string");
+assert.throws(TypeError, () => with_.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => with_.apply(1, args), "1");
+assert.throws(TypeError, () => with_.apply({}, args), "plain object");
+assert.throws(TypeError, () => with_.apply(Temporal.PlainTime, args), "Temporal.PlainTime");
+assert.throws(TypeError, () => with_.apply(Temporal.PlainTime.prototype, args), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/builtin.js
new file mode 100644
index 0000000000..53ffd00651
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.with
+description: >
+ Tests that Temporal.PlainTime.prototype.with
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainTime.prototype.with),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.prototype.with),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.prototype.with),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.prototype.with.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/copy-properties-not-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/copy-properties-not-undefined.js
new file mode 100644
index 0000000000..4ea654c715
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/copy-properties-not-undefined.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.with
+description: PreparePartialTemporalFields copies only defined properties of source object
+info: |
+ 4. For each value _property_ of _fieldNames_, do
+ a. Let _value_ be ? Get(_fields_, _property_).
+ b. If _value_ is not *undefined*, then
+ ...
+ iii. Perform ! CreateDataPropertyOrThrow(_result_, _property_, _value_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(9, 45, 55);
+
+TemporalHelpers.assertPlainTime(plainTime.with({ hour: 8, second: undefined }),
+ 8, 45, 55, 0, 0, 0,
+ "only the properties that are present and defined in the plain object are copied"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..6827322b34
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/infinity-throws-rangeerror.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaintime.prototype.with
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => instance.with({ [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.with({ [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/length.js
new file mode 100644
index 0000000000..b51dcb2cf9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.with
+description: Temporal.PlainTime.prototype.with.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.with, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/name.js
new file mode 100644
index 0000000000..e37f7e45de
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.with
+description: Temporal.PlainTime.prototype.with.name is "with".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.with, "name", {
+ value: "with",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/not-a-constructor.js
new file mode 100644
index 0000000000..4446eb754b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.with
+description: >
+ Temporal.PlainTime.prototype.with does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainTime.prototype.with();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.prototype.with), false,
+ "isConstructor(Temporal.PlainTime.prototype.with)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-invalid.js
new file mode 100644
index 0000000000..7cd1abbf1a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-invalid.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.with
+description: TypeError thrown when a primitive is passed as the options argument
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(12);
+for (const badOptions of [null, true, "hello", Symbol("foo"), 1, 1n]) {
+ assert.throws(TypeError, () => plainTime.with({ hour: 3 }, badOptions));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-object.js
new file mode 100644
index 0000000000..25a0d37bfa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-object.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.with
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+
+const result1 = instance.with({ minute: 45 }, {});
+TemporalHelpers.assertPlainTime(
+ result1, 0, 45, 0, 0, 0, 0,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.with({ minute: 45 }, () => {});
+TemporalHelpers.assertPlainTime(
+ result2, 0, 45, 0, 0, 0, 0,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-undefined.js
new file mode 100644
index 0000000000..02c8659e12
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-undefined.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.with
+includes: [temporalHelpers.js]
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const fields = { minute: 60 };
+
+const explicit = time.with(fields, undefined);
+TemporalHelpers.assertPlainTime(explicit, 12, 59, 56, 987, 654, 321, "explicit");
+
+const implicit = time.with(fields);
+TemporalHelpers.assertPlainTime(implicit, 12, 59, 56, 987, 654, 321, "implicit");
+
+const lambda = time.with(fields, () => {});
+TemporalHelpers.assertPlainTime(lambda, 12, 59, 56, 987, 654, 321, "lambda");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-wrong-type.js
new file mode 100644
index 0000000000..f15373d377
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-wrong-type.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.with
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainTime();
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.with({ minute: 45 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/order-of-operations.js
new file mode 100644
index 0000000000..fdc9c6cb57
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/order-of-operations.js
@@ -0,0 +1,59 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.with
+description: Properties on an object passed to with() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const expected = [
+ // RejectObjectWithCalendarOrTimeZone
+ "get fields.calendar",
+ "get fields.timeZone",
+ // ToTemporalOverflow
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+ // ToTemporalTimeRecord
+ "get fields.hour",
+ "get fields.hour.valueOf",
+ "call fields.hour.valueOf",
+ "get fields.microsecond",
+ "get fields.microsecond.valueOf",
+ "call fields.microsecond.valueOf",
+ "get fields.millisecond",
+ "get fields.millisecond.valueOf",
+ "call fields.millisecond.valueOf",
+ "get fields.minute",
+ "get fields.minute.valueOf",
+ "call fields.minute.valueOf",
+ "get fields.nanosecond",
+ "get fields.nanosecond.valueOf",
+ "call fields.nanosecond.valueOf",
+ "get fields.second",
+ "get fields.second.valueOf",
+ "call fields.second.valueOf",
+];
+const actual = [];
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ hour: 1.7,
+ minute: 1.7,
+ second: 1.7,
+ millisecond: 1.7,
+ microsecond: 1.7,
+ nanosecond: 1.7,
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ overflow: "constrain",
+}, "options");
+
+const result = instance.with(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/overflow-invalid-string.js
new file mode 100644
index 0000000000..f5e60db95f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/overflow-invalid-string.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.with
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.plaintime.prototype.with step 11:
+ 11. Let _overflow_ be ? ToTemporalOverflow(_options_).
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12);
+
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => time.with({ minute: 45 }, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/overflow-undefined.js
new file mode 100644
index 0000000000..1d44db4ab3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/overflow-undefined.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.with
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.plaintime.prototype.with step 11:
+ 11. Let _overflow_ be ? ToTemporalOverflow(_options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12);
+const explicit = time.with({ minute: 67 }, { overflow: undefined });
+TemporalHelpers.assertPlainTime(explicit, 12, 59, 0, 0, 0, 0, "default overflow is constrain");
+const implicit = time.with({ minute: 67 }, {});
+TemporalHelpers.assertPlainTime(implicit, 12, 59, 0, 0, 0, 0, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/overflow-wrong-type.js
new file mode 100644
index 0000000000..96e3ca15dd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/overflow-wrong-type.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.with
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.plaintime.prototype.with step 11:
+ 11. Let _overflow_ be ? ToTemporalOverflow(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12);
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => time.with({ minute: 45 }, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainTime(result, 12, 45, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/plaintimelike-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/plaintimelike-invalid.js
new file mode 100644
index 0000000000..4b9f9c0316
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/plaintimelike-invalid.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.with
+description: Throws TypeError on an argument that is not a PlainTime-like property bag
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const tests = [
+ // Step 3.
+ [undefined],
+ [null],
+ [true],
+ ["2019-05-17"],
+ ["2019-05-17T12:34"],
+ ["2019-05-17T12:34Z"],
+ ["18:05:42.577"],
+ ["42"],
+ [Symbol(), "symbol"],
+ [42, "number"],
+ [42n, "bigint"],
+
+ // Step 4.
+ [Temporal.PlainDate.from("2019-05-17"), "PlainDate"],
+ [Temporal.PlainDateTime.from("2019-05-17T12:34"), "PlainDateTime"],
+ [Temporal.PlainMonthDay.from("2019-05-17"), "PlainMonthDay"],
+ [Temporal.PlainTime.from("12:34"), "PlainTime"],
+ [Temporal.PlainYearMonth.from("2019-05-17"), "PlainYearMonth"],
+ [Temporal.ZonedDateTime.from("2019-05-17T12:34Z[UTC]"), "ZonedDateTime"],
+
+ // Step 5-6.
+ [{ hour: 14, calendar: "iso8601" }, "calendar"],
+
+ // Step 7-8.
+ [{ hour: 14, timeZone: "UTC" }, "timeZone"],
+
+ // Step 9.
+ [{}, "empty object"],
+ [{ hours: 14 }, "only plural property"],
+];
+
+for (const [value, message = String(value)] of tests) {
+ assert.throws(TypeError, () => plainTime.with(value), message);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/prop-desc.js
new file mode 100644
index 0000000000..e4a1c0ca44
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.with
+description: The "with" property of Temporal.PlainTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.prototype.with,
+ "function",
+ "`typeof PlainTime.prototype.with` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime.prototype, "with", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/subclassing-ignored.js
new file mode 100644
index 0000000000..a9112670f6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/subclassing-ignored.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.with
+description: Objects of a subclass are never created as return values for with()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainTime,
+ [12, 34, 56, 987, 654, 321],
+ "with",
+ [{ nanosecond: 1 }],
+ (result) => TemporalHelpers.assertPlainTime(result, 12, 34, 56, 987, 654, 1),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/second-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/second-undefined.js
new file mode 100644
index 0000000000..5eceb3c5ee
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/second-undefined.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime
+description: Second argument defaults to 0 if not given
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const args = [12, 34];
+
+const explicit = new Temporal.PlainTime(...args, undefined);
+TemporalHelpers.assertPlainTime(explicit, ...args, 0, 0, 0, 0, "explicit");
+
+const implicit = new Temporal.PlainTime(...args);
+TemporalHelpers.assertPlainTime(implicit, ...args, 0, 0, 0, 0, "implicit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/subclass.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/subclass.js
new file mode 100644
index 0000000000..4fae03ce38
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/subclass.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime
+description: Test for Temporal.PlainTime subclassing.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomPlainTime extends Temporal.PlainTime {
+}
+
+const instance = new CustomPlainTime(12, 34, 56, 987, 654, 321);
+TemporalHelpers.assertPlainTime(instance, 12, 34, 56, 987, 654, 321);
+assert.sameValue(Object.getPrototypeOf(instance), CustomPlainTime.prototype, "Instance of CustomPlainTime");
+assert(instance instanceof CustomPlainTime, "Instance of CustomPlainTime");
+assert(instance instanceof Temporal.PlainTime, "Instance of Temporal.PlainTime");
+
+reportCompare(0, 0);